DDD Customer, Contacts, and Addresses (aggregate root)
Asked Answered
T

1

7

I'm building an application that manages most of the LOB stuff at my company. I'm trying to wrap my head around DDD... starting with customer management. Many examples are very, very simple in regards to the domain model which doesn't help me much.

My aggregate root is a Customer class, which contains a collection of Addresses (address book), a collection of Contacts, and a collection of communication history.

Seems like this aggregate root is going to be huge, with functions to modify addresses, contacts (which can have x number of phone numbers), and communication.

E.G.

UpdateCustomerName(...)
SetCustomerType(...) // Business or individual
SetProspect(...) // if the customer is a prospect
SetDefaultPaymentTerms(...) // line of credit, etc. for future orders
SetPreferredShippingMethod(...) // for future orders
SetTaxInfo(...) // tax exempt, etc.
SetCreditLimit(...)
AddAddress(...)
RemoveAddress(...)
UpdateAddress(...)
VerifyAddress(...)
SetDefaultBillingAddress(...)
SetDefaultShippingAddress(...)
AddContact(...)
UpdateContact(...)
RemoveContact(...)
SetPrimaryContact(...)
AddContactPhoneNumber(...)
RemoveContactPhoneNumber(...)
UpdateContactPhoneNumber(...)
AddCommunication(...)
RemoveCommunication(...)
UpdateCommunication(...)
etc.

I've read that value objects don't have identity. In this system, each address (in the database) has an ID, and has a customerId as the foreign key. If Address is it's own aggregate root, then I wouldn't be able to have my business logic for setting default billing / shipping. Many examples have value objects without an ID... I Have no idea how to persist the changes to my Customer table without it.

Anywho, feels like I'm going down the wrong path with my structure if its going to get this ginormous. Anyone do something similar? Not sure how I can break down the structure and maintain basic business rules (like making sure the address is assigned to the customer prior to setting it as the default billing or shipping).

Tahitian answered 6/7, 2016 at 18:33 Comment(0)
I
12

The reason that you're butting up against the issue of where business logic should lie is because you're mixing bounded contexts. LoB applications are one of the typical examples in DDD, most of which show the application broken up into multiple bounded contexts:

  • Customer Service
  • Billing
  • Shipping
  • Etc.

Each bounded context may require some information from your Customer class, but most likely not all of it. DDD goes against the standard DRY concept when approaching the definition of entities. It is OK to have multiple Customer classes defined, one for each bounded context that requires it. In each bounded context, you would define the classes with properties and business logic to fulfill the requirements within that bounded context:

  • Customer Service: Contact information, contact history
  • Billing: Billing address, payment information, orders
  • Shipping: Line items, shipping address

These bounded contexts can all point to the same database, or multiple databases, depending on the complexity of your system. If it is the same database, you would set up your data access layer to populate the properties required for your bounded context.

Steve Smith and Julie Lerman have a fantastic course on Pluralsight called Domain-Driven Design Fundamentals that covers these concepts in depth.

Issykkul answered 6/7, 2016 at 18:49 Comment(7)
Great explanation. Thank you.Tahitian
Now I just need to figure how to synchronize between data models loaded in separate bounded contexts (I'm not using event sourcing). E.G. a billing Customer domain model would need a list of addresses to ensure that the default billing address being assigned actually exists... however the addresses would be managed in the customer service bounded contextTahitian
The functionality for updating should be in whichever context those updates occur. The point is that you can create a model for the billing context that just has a property for populating the default billing address. It no updates occur in the billing context then you don't need that functionality there.Issykkul
As far as keeping the contexts up to date, that depends on your database design. Do you have a single database that is being queried by multiple contexts? If so, each context will be consistent. In that Pluralsight course, they discuss different options for database design but its really based on preference. I see you have EF experience so I'll use that as an example. You could have different EF DbContexts defined for each bounded context. In each, you only make the tables available which you will be querying.Issykkul
If you use CodeFirst, you would still need to maintain a single DbContext that is used for migrations that contains all DB Entities.Issykkul
Single databases, traditional 4nf. Using servicestack and mapping pocos to domain model. I'll check out that videoTahitian
I highly recommend it. It's one of the most influential courses I've watched when it comes to software design. Best of luck!Issykkul

© 2022 - 2024 — McMap. All rights reserved.