Bounded context implementation and design
Let's say I have two bounded contexts, the Shipping Context and the Billing Context. Each of these contexts need to know about the customer.
At a data level, the customer is represented by a CustomerTbl table in a database. This table consists of all the necessary columns that describe the customer.
Columns in CustomerTbl (simplified):
The Shipping Context is concerned with Name and PhysicalAddress while the Billing Context is concerned with Name and PaymentMethod.
In the Shipping Context I have modeled the aggregate Recipient:
- Recipient now has properties/value objects for Name and PhysicalAddress
In the Billing Context I have modeled the aggregate Payer:
- Payer has properties/value objects for Name and PaymentMethod
Both Recipient and Payer aggregates are completely separated by the context boundary. They also have their own repositories.
Is it acceptable to have multiple aggregates (provided that they are in separate bounded contexts) using the same "database table"?
Customer data would likely be needed in many more bounded contexts. Would this not mean many aggregate, repository and factory implementations for each bounded context? There will be a degree of redundancy in code. Does this not effect maintainability?
Is it acceptable to have shared properties across aggregates? An example would be the customer Name property. This would also mean redundant validation code?
1) Is it acceptable to have multiple aggregates (provided that they are in separate bounded contexts) using the same "database table"?
- It is acceptable to me as long as you follow a strict strategy for managing the shared state.
- It is acceptable to DDD, since the domain is considered more important than the data.
- It is acceptable to your customer as long as it gets the job done without introducing (too much) hidden costs.
2) Customer data would likely be needed in many more bounded contexts. Would this not mean many aggregate, repository and factory implementations for each bounded context? There will be a degree of redundancy in code. Does this not effect maintainability?
Not necessarily, have a look at the shared kernel pattern.
3) Is it acceptable to have shared properties across aggregates? An example would be the customer Name property. This would also mean redundant validation code?
Same answer as question 1. Concerning redundancy, once you have a shared kernel you may simply put your validator classes in there.
About your conflict between DRY and DDD:
Elegant code does not unnecessarily put great design principles at odds with each other. Usually there is a way that satisfies the principles. You simply haven't found it yet, because either you don't understand the principles enough or you haven't dissected your problem enough.
(if this sounds dogmatic that is because software engineering is actually a religion)
1.Yes if the aggregates and their repositories are read only. Sharing storage and schema between bounded contexts may be a problem if one change the schema or data alone without notifying the others, but I think it's not a big problem in this case.
But this is a different story if the receipient and payer are editable. Chances are that one could modify them, and this causes effect across bounded contexts. For example, the customer may require changing the address of a particular shipping order, but sharing cutsomer tables may change all addresses of the customers shipping orders without notice. So better use different storage for them.
2.Different bounded contexts need different aspect of the customer. I think it's a good practice to depend on necessary data only, so the aggregate abstraction may be different. The potential maintainability problem may occur on the implementation side(mainly because duplicate codes). So we may introduce an extra component returning all aspects of customer and build bounded context specific adapters on it.