Reputation: 815
I am new to DDD and I would like to have some advice on a few challenges I am facing in the implementation of it.
I am using Typescript to develop the application. The data is persisted in a Relational DB. We are not following CQRS pattern and our reads and writes happen in the same database.
Let's assume I have an aggregate User
roughly like below,
class User extends AggregateRoot {
id: number;
phone: Phone;
email: Email;
address: Address;
private constructor(id, phone, email, address){
//setting the values
}
public static create(props) {
return new User({...props});
}
public static update(props) {
return new User({...props});
}
}
Here, Phone
and Email
are ValueObjects
and Address
is an Entity
.
class Phone extends ValueObject {
phNumber: string;
private constructor( ph ) {
phNumber = ph;
}
public static create(ph){
//do validations
return new Phone(ph);
}
}
The class Email
is also similar to Phone
.
Now, once the update phone request is received in the controller, the request is forwarded to the User Service
layer and the service will look roughly like this,
public updatePhone( updatePhNoDto ) {
const userEntity = userRepository.getUser(updatePhNoDto.userId);
const userModel = User.update({
id: updatePhNoDto.userId,
phone: Phone.create(userEntity.phone),
email: Email.create(userEntity.email),
address: Address.create(userEntity.address)
});
userRepository.updateUser(userModel)
}
Here each time the user requests for updating the phone number, I am fetching the user data from the RDBMS and doing all the validations for all the fields which are already validated and then calling the method User.update()
.
So, here are my questions:
Address
entity be an Aggregate Root
by itself? If yes, how should it be handled if both UserInfo and Address are requested to be updated in a single http-request?Please let me know if you find any other flaws in the design.
Thanks!
Upvotes: 1
Views: 1140
Reputation: 2542
Just like the controller has an UpdatePhone endpoint, the User will have an UpdatePhone method that only verifies and updates the phone number. The User AR will also have UpdateEmail, UpdateAddress, etc.
If a user can change multiple User properties at a time on the front end, you use the Controller to figure that out. You would have an UpdateUser endpoint on the controller that would decide what was changed and what wasn’t, then call all necessary methods on the User. Some pseudo code:
If (PhoneInfoUpdated) User.UpdatePhone({user submitted phone fields});
If (EmailInfoUpdated) User.UpdateEmail({user submitted email field});
If (AddressInfoUpdated) User.UdateAddress({user submitted address info});
(You probably just deleted this for brevity in the post, but remember there are 2 levels of data validation here. The controller validates data types, such that if you’re expecting integers you actually get integers, dates are dates, phone numbers and emails are the proper format, etc. Then inside the User.UpdateWhatever method you validate that business rules are satisfied, such as email address is not a duplicate of an existing one, etc.)
I don’t understand how an address can have a life of its own without being owned by a User, but if that is your business case then it should be an AR. Therefore to change the Address you should have a separate Address API endpoint that does the proper manipulations instead of trying to send that through the User endpoint. The front end should make this decision of the proper endpoint to call if it is calling APIs directly, or if you're using MVC the controller receives the postback and can then either call the proper APIs or appropriate methods on the ARs.
As for deletions, I’ve never been a fan of actual deletion so I would recommend adding an Active flag (or Deleted flag depending on which side of that interminable debate you are on). Whether you actually delete or just set a flag, you should have a User.Delete method. If you are really deleting the row, I prefer that to be a static method on the User class so you don’t have to retrieve a user just to then delete it. If you are using a flag, the Delete method should be public on the class because it’s really just setting a property like any other property.
Upvotes: 1