0

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:

  1. Not sure if the above is the right way since I am validating the stuffs that I have already validated and possibly an unnecessary DB call. So, please suggest me on the best practices to deal with the situations like this where a single or only a few fields are being requested to be updated.
  2. A user can update his Address independent of his other information. So, should the 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?
  3. What is the role of the Aggregate Root in deletion? How should that be modeled inside it?

Please let me know if you find any other flaws in the design.

Thanks!

Praveen Kumar
  • 977
  • 3
  • 12
  • 26

1 Answers1

1

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 date 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 it’s 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 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.

Brad Irby
  • 2,397
  • 1
  • 16
  • 26
  • "I don’t understand how an address can have a life of it’s own without being owned by a User" - Yes, I felt the same. Hence kept it as a separate entity within the User AR. so, just to update the address, like you said, will it be OK to have a method like `UpdateAddress` in the AR? – Praveen Kumar Oct 21 '19 at 10:41
  • Yes, the User.UpdateAddress is the best way to go. – Brad Irby Oct 21 '19 at 11:14
  • Great. Now this creates another doubt. I read somewhere that all the domain changes should go through the aggregate root and the aggregate root should be created as whole and not in pieces. Your thoughts on this? – Praveen Kumar Oct 24 '19 at 08:00
  • Yes, that is a hard and fast rule you should not try to get around. All business and validation logic should be encapsulated in that AR. So if you try to slip in a change in at a lower level "for performance reasons" then others on the team (future team members?) will not know that is there and will find data changes mysteriously happening. Also, if validation or business rules change in the future, making changes outside of the context of the AR will not enforce those rules. – Brad Irby Oct 24 '19 at 08:57
  • With that said, remember that it's OK to have entities that are ARs in one context and value objects in another. Imagine an invoice line item. That is an AR because it requires the logic to check inventory, pricing, SKUs, etc. But it's also a value object in the context of the order that owns it. An order will be created with a list of LineItem value objects because Order doesn't know how to change a line item. For Order to change the quantity of a line item, it must create a LineItem AR and tell it to change the quantity. – Brad Irby Oct 24 '19 at 09:01
  • One more thought - when I say an entity is a Value Object in one context and an AR in another, these are 2 different classes. Both can read from the same data table (or the VO can add data from another table that's convenient to have available, such as QuantityOnHand from the Inventory table), so you would have LineItemAR class and LineItemVO class. The Order is populated with a list of LineItemVO, and to make a change to a line item it creates a new LineITemAR, calls the proper method, then regenerates its list of LineITemVO entities. – Brad Irby Oct 24 '19 at 09:03