I would like your advices about bounded contexts integration.
I have a usecase which put me in a corner :
I have a bounded context for Contract management. I can add parties (various external organizations for example) to a contract. Select for each party their investment / contribution (ex: 10% of the total). SO contract management is two-fold : one is administrative (add party, manage multiples dates, ...) the other one is financial (plan their contributions that span multiple years, check contributions consumption, ...).
I have another bounded context for Budget. This context is responsible for expenses management at the organisation level. Example: a service A will have 1000 € of expense capacity. We can plan a budget and after that each organisation party can consume, buying stuff, their part. In order to build a budget, the user in charge of the enterprise budget can allocate money directly or integrate a yearly contract financial component. When we integrate a contract part inside the budget we froze the data inside the budget, i.e we copy the monetary data from one database table inside another one (adding some audit informations). We have a single database.
It is this last part I struggle with. Each bounded context is a dedicated application. In the budget application, after a contract part has been integrated inside the current budget, I need to display the budget details lines. Unfortunately in the budget tables I have only the money data and not some basic info about the contract (object, reference, ...).
What am I thinking :
sometimes is not bad to duplicate data between bounded contexts. I froze the money part of a contract. I can also freeze / duplicate the object and reference of the contract. Then the querying will only take place inside the budget context. But what is problematic here is the data duplication. Today I need object /refrerence and if tomorrow I need more fields ... I will need domain events management to keep the data between contract / budget in sync.
querying budget and for each line query a contract service that will return the data needed. That keep each context autonomous but I need to make lots of database requests to enrich the budget details line objects.
with only one join at the database level we can make this work. What about coupling here ? It is the simple solution and what we are doing today (is it a shared kernel ?). It seems we can't afford to change contract structure without rebuilding the budget application. I don't have a programmatic contract between the contexts.
My question is :
How can I build this UI screen that need data from the budget context and each details line need data from the contract context ?
Side Notes :
Perhaps the contexts identification and perimetre are wrong from the start (it is a legacy design).
What I would like is to keep the context separate (loose coupling). If we can specify design contracts between the contexts, the maintenance is easier (or not ?).
I failed to see how to integrate these contexts (I need to re-read shared kernel, ustream / downstream etc).
This is an additional, distinct bounded context. It has some overlap with the existing bounded contexts, which can easily lead you down the wrong path (merging contexts or putting additional behaviour in a context where it doesn't belong).
Sometimes it's OK to have entities in different bounded contexts which are referring to the same logical entity, but which are just providing a different view of that entity for the purposes of a specific scenario (eg in a specific context).
A good example of this is in an e-commerce scenario. In most e-commerce applications you will have the concept of an Order, but there is no global, definitive notion of what an "order" is. In a finance context - the order is simply an invoice. In a fulfilment context - the order is simply a packing list and an address to send the goods to. In a marketing context - the order represents a little piece of intelligence about what the customer is interested in, which can be used for future targeted marketing.
There is a thread of commonality which runs through all of those entities, but you would likely see at least 3 separate Order classes, each one capturing the concept of an order within a context.
And so in your case, you have a bounded context for Contract and a bounded context for Budget. It seems to me that you now have another way of looking at these entities, and specifically the way in which they interact with each other. This is a new view of the entities, a view which can be captured in its own context. This new context will likely have its own Contract and Budget entities, and there will be overlap with the Context and Budget contexts, but there will also be additional relationships and behaviour in there, which wouldn't make sense in those other contexts.
This is a really difficult idea to explain :) I wrote an answer to a similar question some time ago here: DDD - How to design associations between different bounded contexts
Related
I'm developing a budgeting app using Domain Driven Design. I'm new to DDD and therefore need a validation of my design.
Here are the concepts I came up with:
Transaction - which is either income or expense, on annual or monthly or one-off etc. basis.
Budget - which is the calculated income, expenses and balance projection, divided into occurrences (say e.g. 12 months over the next year, based on the Transactions).
I made the Transaction the Entity and Aggregate Root. In my mind it has identity, it's a concrete planned expense or income that I know I'll receive, for a concrete thing, and I also need to persist it, so I can calculate the budget based on all my transactions.
Now, I have an issue with the Budget. It depends on my concrete list of Transactions. If one of the Transactions gets deleted, the budget will need to be re-calculated (seems like a good candidate for a domain event?). It's a function of my identifiable transactions at any given time.
Nothing outside the Aggregate boundary can hold a reference to anything inside, except to the root Entity. Which makes me think the budget is the Aggregate Root as it cannot be a ValueObject or Entity within the Transaction.
What's confusing is that I don't necessarily need to persist the budget (unless I want to cache it). I could calculate it from scratch on request, and send it over to the client app. 2 different budgets could have the same number of occurrences, incomes, expenses and balances (but not Transactions). Perhaps an argument for making it a ValueObject?
So, my questions is - what is the Budget?
Domain context vs Aggregate
First element you get wrong is a point of details about DDD semantics. If there is only one object in your "aggregate", then it is not an aggregate. An aggregate is a structure made of multiple (2+) objects, with at least one being an entity and called the aggregate root. If a TransactionRpository returns a Transaction object that has no value object or entity, then Transaction is an entity but not an aggregate nor an aggregate root. If a BudgetRepository returns a Budget entity that includes a Transaction object, then Budget and Transaction form an aggregate, Budget being the aggregate root. If Budget and Transaction are returned from different repositories, then they form different contexts.
Context being the generic concept that can either be an aggregate or an entity.
Contexts are linked to use cases
Second element you get wrong is that you are trying to design your domain model outside of your use cases context. Your application clearly manipulates both concepts of Budget and Transactions, but does your application handles uses cases for both (budget management and transaction management) ? If yes, are these uses case different in a way that implies different domain constraints ?
If your application only handles Budget management, or both but they share their business constraints, then you only need a single context, that manipulates both concepts in a single aggregate. In that situation, Budget is probably your root aggregate, and it's up to your mode and use cases to tell whether the Transaction is a value object or you need to access them by Id.
If your application handles uses cases for both, with different business constraints, then you should split your domain in two contexts, with two different models, one for the Budget management use cases, the other for the Transaction management use cases.
Polysemic domain model
The third element you get wrong, is that you are trying to build a single, unified, normalized domain model. This is wrong because it introduces very complex structures, and a lot of business rules that are irrelevant to your business cases. Why would you need to manipulate the Budget domain model when the use case does not need knowledge of the Budget concept or linked business rules ?
If your application has use cases for both concepts, you need two models. The Budget management model should not use the Transaction management model. However, that does not implies that the Budget model is not allowed to manipulate the Transaction concept and vice versa. It only means you must write another model for that. You could have a Budget context that manipulates Budget and BudgetTransaction models, and Transaction context that manipulates Transaction and TransactionBudget models. These models can map to the same RDBMS tables with different columns, relevant to their use cases, implementing relevant business rules.
This is called writing a polysemic domain model.
Conclusion
So, my questions is - what is the Budget?
It is not possible to answer definitely your last question, as the answer depends on the use cases your application handles. However, you mention the following constraint:
If one of the Transactions gets deleted, the budget will need to be re-calculated
This seems a very good argument in favor of making your application as a single context application, based on an aggregate with Budget being the aggregate root and Transaction being an entity in the aggregate.
If you don't need to, try to refrain from splitting these two concepts in different contexts, unless you have very good reasons to do so: they manipulate excluding columns, they manipulate excluding business rules, you are interested in deploying these two in different bounded contexts, different services, as they would scale differently, etc ...
Having business constraints that span accross multiple contexts implies a complex implementation based on domain events, 2-phase commits, saga pattern, etc ... It's a lot of work, you should balance that work with the benefits you expect in return.
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).
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.
Meanwhile, I learned that Aggregate Roots should be wired explicitly by identifiers, not by actual associations to the other AR.
What if, for technical reasons, the identifiers of a same "thing" are different ones because I am not able to influence them?
Look at this picture for example,
(Source: http://martinfowler.com/bliki/BoundedContext.html
Imagine the Support Context is a total different third party system and uses its own identifiers, and I am not able to put my identifier in that system in any way.
So, in my Sales Context I am the master of the identifiers, my Customer and Product have identifiers which I gave; and I need to somehow wire the Sales Context's Customer to the Support Context's Customer.
Where would that adaption take place?
The most likely place is the anti corruption layer of the Sales Bounded Context under your control. You can keep a simple mapping around that translates from and to the Support Bounded Context identifiers. The relationship between these contexts will influence how you'll go about it. I suppose a product is sprouted from R&D and makes it into something that is put in the catalog by sales which - once sold - will need to be supported. Granted, this is guess work on my behalf.
I have Meeting objects that form the basis of a scheduling system, of which gridviews are used to display the important information. This is for the purpose of scheduling employees to meetings, and for employees to view what has been scheduled.
I have been trying to follow DDD principles, but I'm having difficulty knowing what to pass from my service layer down to presentation area of system. This is because the schedule can be LARGE, and actually consists of many different elements of the system. Eg. Client Name, Address, Case Info, Group,etc, all of which are needed for the meeting scheduler to make a decision.
In addition to this, the scheduler needs to change values within this schedule and pass it back up to the service layer (eg. assign employees from dropdowns, maybe change group, etc). So, the information isn't really "readonly" - it needs to be interacted with. ie. It's not just a report.
Our current approach is to populate a flattened "Schedule Object" from SQL, which is constructed from small parts of different domain objects. It's quite a complex query. When changes have been made, this is then passed back up to the service layer, and the service will retrieve the domain objects in question, and fire business methods on the domain objects using information from the DTOs.
My question is, is this the correct approach? ie. Continue to generate large custom objects from SQL, and then pass down from Service Layer to Presentation Layer objects that feel a lot like View Models?
UPDATE due to an answer
To give a idea of the amount entities / aggregates relationships involved. (this is an obfuscated examples, so relationships are the important things here)
Client is in one default group
Client has one open case but many closed
Cases have many Meetings
Meeting have many assigned Employees
Meeting have many reasons
Meeting can get scheduled to different groups
Employees can be associated with many groups.
The schedule need to loads all meetings in open cases that belong to patients who are in the same groups as the employee.
Scheduler can see Client Name, Client Address, Case Info, MeetingTime, MeetingType, MeetingReasons, scheduledGroup(s) (showstrail), Assigned Employees (also has hidden employee ids).
Editable fields are assign employee dropdowns and scheduled group.
Schedule may be up to two hundred rows.
DTO is coming down from WCF, so domain model is accessed above this service layer, and not below.
Domain model business calls leveraged by service based on DTO values passed back, and repositories deal with inserts/updates.
So, I suppose to update, is using a query to populate an object which contains all of the above acceptable to pass down as one merged DTO? And if not, how would you approach it? ( giving some example calls to service layer, and explaining a little bit about how you conceive the ORM fetching the data keeping in mind performance)
In the service layer and below, I would treat each entity (see aggregate roots in DDD) separate with respect to it's transactional boundary. I.e. even if you could update a client and a case in the same UI view, it would be best to transactionally modify the client and then modify the case. The more you try to modify in one transaction, the more you can conflict with other users.
Although your schedule is large and can contain lots of objects, the service layer should again deal with each entity (aggregate root) separately and then bundle them together into a new view model. Sadly, on brown-field projects, a lot of logic might be in the SQL and the massive multi-table joins might make this harder to refactor into more atomic queries that do exactly what is needed. The old-school data-centric view of 'do everything you can in the database' goes against everything DDD.
Because DDD is a collection of design ideas and patterns and not particularly a methodology or an architecture, it sounds that it might be too late to try shoe-horn your current application into a DDD application-centric design. It sounds as though your current app is very entrenched in the data-centric view.
If everything is currently being passed up through the layers in one monolithic chunk, it might be best to keep with this style and just expose these monolithic chunks to the people in the other team who wish to consume them, for use in their new app. You might be able to put some sort of view model caching in place (a bit like the caching view model element in CQRS).
In my personal opinion, data-centric, normalised data apps have had their day (they made sense in the 1970s when hard disk space was expensive) and all apps should be moving toward more modern practices. In reality, only when legacy systems are crawling on their knees, will stakeholders usually put up the cash to look for alternatives (usually after stuffing every last server with RAM). It might be possible or best to convince them to refactor small sections at a time.
In my current project (e-commerce website), we have different Bounded Context like: billing, delivery or payment in our checkout process.
On top of this, depending on what the customer will buy, the checkout process will be different. So depending on the content of her cart the number of steps in the checkout process can be different, or we won't/will ask her for certain informations.
So should one create a different bounded context for each different type of checkout process ?
For example, the Order aggregate root will be different depending on the checkout process
EticketsOrder (in this context we don't need a delivery address so we won't ask one to the user)
Ticket BillingAddress
ClothesOrder (in this context we need a delivery address and there will be an additional step in the checkout process to get this)
Clothes BillingAddress DeliveryAddress
This separation will imply to create two different domain entities even thought they have similar properties.
What's the best way to model this kind of problem ? How to find the context boundary ?
A bounded context is chiefly a linguistic boundary. A quote from the blue book (highlighted key part):
A BOUNDED CONTEXT delimits the applicability of a particular model so
that team members have a clear and shared understanding of what has
to be consistent and how it relates to other CONTEXTS. Within that
CONTEXT, work to keep the model logically unified, but do not worry
about applicability outside those bounds. In other CONTEXTS, other
models apply, with differences in terminology, in concepts and rules,
and in dialects of the UBIQUITOUS LANGUAGE.
A question to ask is whether the different types of orders created are entirely distinct aggregates, or are they all order aggregates with different values. Is there a need to consider order as a whole regardless of how they were created? I've build and worked with ecommerce systems where different types of orders were all modeled as instances of the same aggregate, just with different settings and there were no linguistic issues. On the other hand, the orders in your domain may be different enough to warrant distinct contexts.
I often consider BC boundaries from the perspective of functional cohesion. If you segregate orders into two BCs will there be a high degree of coupling between them? If so, that may be a sign that they should be combined into one BC. On the other hand, if the only place that the BCs interact is for reporting purposes, there is no need to combined them.
It appears as though you may have missed a bounded context. When this happens one tends to try and fit the functionality into an existing BC. The same thing happens to aggregate roots. If something seems clumsy or it doesn't make sense try to see whether you haven't missed something.
In your example I would suggest a Shopping BC (or whatever name makes sense). You are trying to fit your checkout process into your Order BC. Your Shopping BC would be responsible for gathering all the data and then shuttling it along to the relevant parts.
The product type selected will determine whether a physical delivery is required.
Hope that helps.