What is the Aggregate root in this case - domain-driven-design

I have these entities
User
Task - every task must have a user assigned
Project - a task can be assigned to a project but it's not necessary, user must be assigned as a project creator
TaskStatus - every task must have a status assigned
and I designed the aggregate roots as follows
Aggregate roots
User with entities:Task, Task status
Project
Am I going in right direction ?

Yeah, sounds quite right. This is my improvisation in c#:
//aggregate root
public class User{/*first name, last name, contacts, etc.*/}
//aggregate root
public class Project{
public IList Tasks{get; private set;}
public User CreatedBy{get; private set;}
public Project(User createdBy){
CreatedBy=createdBy;
}
public RegisterTask(string taskDescription, User assignedTo){
Tasks.Add(new Task(taskDescription, assignedTo));
}
public StartWorkOn(Task task){
if(!Tasks.Contains(task))
throw new Exception("Task is not registered");
task.StartWork();
}
}
//entity
public class Task{
public User AssignedTo{get;private set;}
public string Description{get; private set;}
public TaskStatus Status{get; private set;}
internal Task(string description, User assignedTo){
AssignedTo=assignedTo;
Description=description;
Status=TaskStatus.Pending;
}
internal void StartWork(){
if(Status!=TaskStatus.Pending)
throw new Exception("Can't start what's ongoing");
Status=TaskStatus.InProgress;
}
internal void Finish(){
if(Status!=TaskStatus.InProgress)
throw new Exception("Can't finish what has not started");
Status=TaskStatus.Finished;
}
}
//value object
public enum TaskStatus{ Pending, InProgress, Finished }
var me=new User();
_users.Save(me);
var you=new User();
_users.Save(you);
var project=new Project(me);
project.RegisterTask("Have a nice day!", you);
project.StartWorkOn(project.Tasks.First());
_projects.Save(project);
Keep in mind - that's just a sketch. Don't take it too seriously.

Related

Javafx: Automatic update of table cell using Thread

I have a Trade class which contains a property currentPrice, which downloads price data from a website using getPricedata() method. The Trade object will show up as a table row in TableView. Now, my task: is to
use the getPricedata() method to grab data from internet, populate the currentPrice cell, whenever the object is created.
relaunch the getPricedata() method to every 1 minute after the object has been created and update table cell.
Below is the basic structure of my code. But I have no idea how to implement this ?
Which package do I need ? Task ? Service ? ScheduledService ?
public class Trade{
private DoubleProperty currentPrice;
// need thread here
public double getPricedata(){
.......
}
}
Use a ScheduledService<Number>, whose Task<Number>'s call() method retrieves and returns the value. Then you can either register an onSucceeded handler with the service, or just bind the Trade's currentPrice to service.lastValue(). Call setPeriod(..) on the service (once) to configure it to run every minute.
Since the currentPrice is being set from the service, you should only expose a ReadOnlyDoubleProperty from your Trade class (otherwise you might try to call currentPriceProperty().set(...) or setCurrentPrice(...), which would fail as it's bound).
I would do something like
public class Trade {
private final ReadOnlyDoubleWrapper currentPrice ;
private final ScheduledService<Number> priceService = new ScheduledService<Number>() {
#Override
public Task<Number> createTask() {
return new Task<Number>() {
#Override
public Number call() {
return getPriceData();
}
};
}
};
public Trade() {
priceService.setPeriod(Duration.minutes(1));
// in case of errors running service:
priceService.setOnFailed(e -> priceService.getException().printStackTrace());
currentPrice = new ReadOnlyDoubleWrapper(0);
currentPrice.bind(priceService.lastValueProperty());
startMonitoring();
}
public final void startMonitoring() {
priceService.restart();
}
public final void stopMonitoring() {
priceService.cancel();
}
public ReadOnlyDoubleProperty currentPriceProperty() {
return currentPrice.getReadOnlyProperty();
}
public final double getCurrentPrice() {
return currentPriceProperty().get();
}
private double getPriceData() {
// do actual retrieval work here...
}
}
(Code just typed in here without testing, but it should give you the idea.)

How to create service in C# which will perform some operations asynchronously in scenario mentioned below?

I have WebApi Controller as mentioned below. This controller having Update method which will internally call service called CustomerDataService to Update Customer Records.Assume we have n customer records to update.
UpdateMethod in CustomerDataService will perform update and return the update response.
I have requirement to do some heavy processing asynchronously after the update response like manipulating data / managing the data cache. As this processing is time consuming not relevant to the consumer of this API as Update successfully happens So I have to perform this asynchronously. Can I do this with C# with the given scenario? Please suggest.
Note: I do not want to create any batch job to achieve this as I want to perform operation(s) which are user session specific.
Controller
public class CustomerController : ApiController
{
[HttpGet]
public string UpdateCustomer()
{
ICustomerService obj = new CustomerDataService();
return obj.UpdateCustomer(GetCustomerList());
}
private List<CustomerModel> GetCustomerList()
{
return new List<CustomerModel>()
{
new CustomerModel
{
CustomerId="1",
Name="John",
Category="P1"
},
new CustomerModel
{
CustomerId="2",
Name="Mike",
Category="P2"
}
//....n Records
};
}
}
Model
[Serializable]
[DataContract]
public class CustomerModel
{
[DataMember]
public string CustomerId { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Category { get; set; }
}
Interface and CustomerDataService
public interface ICustomerService
{
string UpdateCustomer(List<CustomerModel> customerList);
}
public class CustomerDataService : ICustomerService
{
public string UpdateCustomer(List<CustomerModel> customerList)
{
//Do Data Processing - DB Call
//Return Confirmation Message
return "Data Updated Successfully!!!";
//Needs to perform some processing asynchronously i.e. Call ProcessResults()
}
private void ProcessResults()
{
//DO Processing
}
}
What you are looking for is using async/await in c#, see this article on Microsofts website: Asynchronous Programming with Async and Await. Here is another article with plenty of examples: C# Async, Await.
Once you understand how this works it will be very easy to change your code to take advantage of this pattern. Let us know if you have specific questions or run into problems.

Injecting HttpContext.Current in MVC Role Provider

I have a class in my MVC5 application that deals with some user related functionality and has a dependency on HttpContext.Current.User as shown below
public interface IUser
{
// return roles of currently logged in user
string[] GetRoles;
}
public Class User : IUser
{
private HttpContext context;
// constructor
public User(HttpContext user)
{
this.context = user
}
// get roles
public string[] GetRoles()
{
string username = this.context.User.Identity.Name;
// get roles through some DB calls
string[] roles = someDbCalls();
return roles;
}
}
I have it setup for dependency injection using Ninject in NinjectWebCommon.cs as
kernel.Bind<IUser>().To<User>().WithConstructorArgument("user", x => HttpContext.Current);
This works fine if called from anywhere in my code except in my custom RolesProvider which is setup as shown below
public class CustomRoleProvider : RoleProvider
{
[Inject]
public IUser user {get; set;}
public override string[] GetRolesForUser(string username)
{
return this.user.GetRoles();
}
}
The call to GetRoles() from my custom role provider fails because HttpContext.Current.User injected by Ninject under this case is null. Any idea on what I may be doing wrong?
Edit:
On further testing, it appears that the problem is with the way I am using Ninject in my custom Roles provider. Using the attribute injection as shown below
[Inject]
public IUser user {get; set;}
works only the first time and subsequent calls fail with HttpContext.Current.User is null error. I have fixed it in a hacky way by forcing the injection to happen each time I call the GetRoles method as shown below
public class CustomRoleProvider : RoleProvider
{
private IUser user;
public override string[] GetRolesForUser(string username)
{
// force ninject to inject a new instance of my interface
var user = DependencyResolver.Current.GetService<IUser>();
return user.GetRoles();
}
}
Not sure why this works and so I am leaving this question open if someone can provide an explanation.
It appears that by the time the role provider is called, the HTTPContext.Current is not yet set. This leads to other issues with custom RolesProvider (like the Null Reference Exception due to EtwTracing bug see: SqlRoleProvider on IIS8 Express
).
If you really need the HTTPContext.Current instead of using the Thread's PrincipalIdentity, you can setup your app to use compatibility mode. This appears to resolve the problem by setting up the HttpContext.Current sooner:
https://social.msdn.microsoft.com/Forums/en-US/8ee88c92-5e8a-4c66-ace7-887eb500e1cb/httpcontextcurrent-always-been-null

How to destroy a session after data is presented in a jsf page

I have this problem: I´m making this wonderfull tutorial The NetBeans E-commerce Tutorial . But instead of make it in JSP as is presented, i´m making a JSF version. Just to undertands the logic in construction an application like that.
In certain part the ControllerServlet.java, has this code:
int orderId = orderManager.placeOrder(name, email, phone, address, cityRegion, ccNumber, cart);
// if order processed successfully send user to confirmation page
if (orderId != 0) {
// dissociate shopping cart from session
cart = null;
// end session
session.invalidate();
// get order details
Map orderMap = orderManager.getOrderDetails(orderId);
// place order details in request scope
request.setAttribute("customer", orderMap.get("customer"));
request.setAttribute("products", orderMap.get("products"));
request.setAttribute("orderRecord", orderMap.get("orderRecord"));
request.setAttribute("orderedProducts", orderMap.get("orderedProducts"));
userPath = "/confirmation";
// otherwise, send back to checkout page and display error
As you can see, the author invalidates the session, in order to permit another purchase order. I made an Managed Bean with session scope in order to mantain the data avalaible throught the whole session. But when I try to clean up the session, as in the tutorial the author does, I can´t receive the data for confirmation.
Then, I made a different managed bean in order to have one to process the order (CartManagerBean), and another one to present the confirmation (ConfirmationMBean). I just injected the confirmatioBean into the cartBean to pass the orderId, necessary to present the data. In the confirmationBean, I made a cleanUp() method that invalidates the session.
But always, the data is not presented. So if any one can tell me what to do, I´ll appreciate.
Here is the part of my cartBean's code that pass the data to the confirmation bean:
...
#ManagedProperty(value ="#{confirmationBean}")
private ConfirmationMBean confirmationBean;
...
public String makeConfirmation() {
FacesContext fc = FacesContext.getCurrentInstance();
if (!cartMap.isEmpty()) {
int orderId = orderManager.placeOrder(name, email, phone, address, credicard, cartMap);
// if order processed successfully send user to confirmation page
if (orderId != 0) {
// get order details
confirmationBean.setOrderId(orderId);
// dissociate shopping cart from session
cartMap.clear();
// end session
//fc.getExternalContext().invalidateSession();
}
}
return "confirmation";
}
As you can see, I commented the part that invalidates the session. Here is the code that I implemented for the ConfirmationMBean:
#ManagedBean(name = "confirmationBean")
#SessionScoped
public class ConfirmationMBean implements Serializable{
private Customer customer;
private List<OrderedProduct> orderedProducts;
private CustomerOrder orderRecord;
private List<Product> products;
private int orderId;
#EJB
private OrderManager orderManager;
public void cleanUp(){
FacesContext fc = FacesContext.getCurrentInstance();
fc.getExternalContext().invalidateSession();
}
private void init(){
Map<String, Object> orderMap = orderManager.getOrderDetails(orderId);
customer = (Customer) orderMap.get("customer");
orderRecord = (CustomerOrder) orderMap.get("orderRecord");
orderedProducts = (List<OrderedProduct>) orderMap.get("orderedProducts");
products = (List<Product>) orderMap.get("products");
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public List<OrderedProduct> getOrderedProducts() {
return orderedProducts;
}
public void setOrderedProducts(List<OrderedProduct> orderedProducts) {
this.orderedProducts = orderedProducts;
}
public CustomerOrder getOrderRecord() {
return orderRecord;
}
public void setOrderRecord(CustomerOrder orderRecord) {
this.orderRecord = orderRecord;
}
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> products) {
this.products = products;
}
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
init();
cleanUp();
}
}
As you can see, when the orderId is setted by the preceding bean, the data is requested from the database, and populates the variables to present in the facelet. ¿Where or how I have to use the cleanUp method in order to obtain the same result that the tutorial?
Thanks in advance.
Put the bean where you're invoking the action in the request scope instead of session scope and get hold of the desired session scoped bean as a (managed) property.
#ManagedBean
#RequestScoped
public class SubmitConfirmationBean {
#ManagedProperty("#{cartBean}")
private CartBean cartBean;
// ...
}
And reference it by #{submitConfirmationBean.cartBean...} instead of #{cartBean...}.
Alternatively, explicitly put the desired session scoped bean in the request scope in the same action method as where you're invalidating the session:
externalContext.getRequestMap().put("cartBean", cartBean);
This way the #{cartBean...} will refer the request scoped one instead of the session scoped one which is newly recreated at that point because you destroyed the session. The request scoped one is lost by next request anyway.

Aggregate root design and presentation layer

I was wondering if how my presentation layer is structured could be a lead to design my aggregate roots.
Lets have an entity ProjectEntity and its related entity ProjectMemberEntity (1:M)
The page is structured as follows:
The top of the page is a form for ProjectEntity
Underneath the form is a grid that shows a list of ProjectMemberEntity.
If a new ProjectMember will be added, the user have to go to this page and click on the button "add new member" which is located in the header of the grid.also edit and delete has the same analogy.
I'm wondering if this behavior/'page structure' could be a hint for a aggregate root(projectentity)
That's a hint for sure. But no more.
Better way to clarify kind of that entity relationship is to ask domain expert:
does project member makes any sense without project?
can member participate in multiple projects?
If those are answered positively, it's highly likely that You should model project member as an aggregate root itself. Otherwise - demote it as an entity that cannot live w/o a project.
Here is some code that might give You some ideas:
public class Project:Root{
private List _members;
public IEnumerable<Member> Members{get {return _members;}}
public string Name{get;private set;}
public bool IsFinished{get;private set;}
public bool FinishedOn{get;private set;}
public Project(string projectName){
_members=new List<Member>();
Name=projectName;
}
public Member AssignMember(string memberName){
var member=new Member(memberName);
_members.Add(member);
return member;
}
public void UnassignMember(string memberName){
var member=_members.First(m=>m.Name==memberName);
if(!member.HasCompletedAllTasks())
throw new Exception
("Cannot unassign member with incompleted tasks!");
_members.Remove(member);
}
public void AssignTaskToMember(string taskName, string memberName){
var member=_members.First(m=>m.Name==memberName);
member.AssignTask(taskName);
}
public void MemberHasCompletedTask(Member member, Task task){
EnsureListContains(_members,member);
EnsureListContains(member.Tasks,task);
task.MarkAsCompleted();
}
public void FinishProject(){
if(_members.Any(m=>!m.HasCompletedAllTasks()))
throw new Exception
("Can't finish project before members have completed all their tasks.");
IsFinished=true;
FinishedOn=DateTime.Now;
}
private void EnsureListContains<T>(IList<T> lst, T itm){
if(!lst.Contains(itm)) throw new Exception();
}
}
public class Member:Entity{
public string Name{get;private set;}
private List<Task> _tasks;
public IEnumerable<Task> Tasks{get{return _tasks;}}
internal Member(string memberName){
Name=name;
_tasks=new List<Task>();
}
internal void AssignTask(string taskName){
_tasks.Add(new Task(taskName));
}
public bool HasCompletedAllTasks(){
return _tasks.All(t=>t.IsCompleted);
}
public Task GetNextAssignedTask(){
return _tasks.Where(t=>!t.IsCompleted)
.OrderBy(t=>t.AssignedOn).First();
}
}
public class Task:Entity{
public string Name{get; private set;}
public bool IsCompleted{get; private set;}
public DateTime CompletedOn{get; private set;}
public DateTime AssignedOn{get; private set;}
internal Task(string name){
Name=name;
AssignedOn=DateTime.Now;
}
internal void MarkAsCompleted(){
if(IsCompleted) throw new Exception
("Task is already completed!");
IsCompleted=true;
CompletedOn=DateTime.Now;
}
}
public class App{
public static void Main(){
var project=new Project
("Question: Aggregate root design and presentation layer");
var me=project.AssignMember("Arnis Lapsa");
project.AssignTaskToMember("Try to help user137348","Lapsa");
var currentTask=me.GetNextAssignedTask();
//SpamStackOverflow();
project.MemberHasCompletedTask(me,currentTask);
if(me.HasCompletedAllTasks()) project.Finish();
else throw new Exception("Enough for today...");
}
}
Keep in mind that I got little knowledge of what Your business is about. This is just an improvisation. :)
When it comes to DDD, make sure you don't get analysis paralysis when trying to design your domain and aggregates. It happened to me. My project got literally STOPPED for a whole month because i wasn't enable to get my aggregates straight. And i am talking about a simple 3 database tables situation. User, Address and UserProfile.
The thing with DDD is that there is no such a thing like DONE THE RIGHT WAY. If you post the same question here with an interval of 3 months from each other, you will always get the "experts" giving you completely different answers in each question. Amis L. was kind enough to give you a solid simple example. Most people would copy and paste from Eric's books.
Do whatever floats your boat. At the end of the day, no matter how handcrafted your domain is, it's never RIGHT to the community. Just chill and enjoy coding.

Resources