What I'm trying to achieve is to develop an application implementing the DDD approach.
The story might sound silly but it's an actual, real life problem. Believe me.
The business looks as follows:
Let's say a company specializes in manufacturing sweets which are distributed to its own shops for sale.
The craftsman makes different types of candy depending on what is - and what is not - currently at the display at one of the company's shop.
When a basket of one flavour 'disappears' the seller replaces this type of sweet with a different kind from the shops storage cabinet.
Duplicates of flavours at the display shouldn't exist and the display should be populated with as much as the capacity allows or how much the manafacturer can handle to produce.
The sweets are distributed from the manufacturer's lab's storage to the shop's one depending on the demand.
Let's assume each worker has public view access to the display and the storage cabinet. Each worker (user) decides on it's own what to provide. The shops display view will be publicly accessible through the application to a potential client as an information what is currently on sale.
So far I have split the business logic into three separate (sub?)domains which are:
Production
Distribution
Sale
And of course each entity like Sweets, Storage, Craftsman, it's Repository etc. are placed respectively in their domain.
The concerns I approach are:
Is it appropriate that an entity (Sweet) is being passed from one domain to another?
Should a Provider be able to reach the StorageCabinet of one domain and pass it's content to another?
Is my reasoning proper? Correct me if I'm wrong or violating any DDD rules.
Thanks in advance.
The story might sound silly but it's an actual, real life problem.
This is great, actually. In his recent retrospective, one of the things that Greg Young called out is that "shopping cart" models are a really lousy as a teaching tool. He points out briefly that the interesting questions are in the supply chain.
Is it appropriate that an entity (Sweet) is being passed from one domain to another?
No, but a message (DTO) describing an entity's state might be passed from one domain to another.
You want to keep the flexibility to define the entities differently in each domain; that's part of the point of identifying bounded contexts.
Should a Provider be able to reach the StorageCabinet of one domain and pass it's content to another?
Probably not: your domain model isn't the book of record for the storage cabinet. Listen very carefully to Greg's comments on one way commands.
Related
I am curious how you would approach it.
Imagine that you operate in the domain of Bikes selling. After workshops with the domain experts, you identified several subdomains (coarse-grained):
At the next workshop (fine-grained) you spotted that there is a further split to the next subdomains (the example is based on offers but it is the same for other coarse-grained subdomains):
So, there is a possibility to subscribe to new offers (or categories of offers) as a customer, there are discounts that can be applied to all offers (e.g. black Friday) or to a specific offer, and publishing of an offer (including approval) that is handled by 2 employees - author and approver.
Now it is time to categorize selected subdomains to:
Core
Supportive
Generic
After categorization, you found out that there is 1 core domain and 2 supportive ones:
Subscriptions - your company is known in the market as the one that has the greatest approach to handling subscriptions to offers. Your algorithm rocks the market and no one is able to do a similar thing. You spotted a Core domain.
Publishing - you were not able to find any existing solution to tackle it and decided to handle it on your own. There is almost no business logic but the data structures are quite complex. You spotted a Supportive domain.
Discounts - same story as with publishing, however here the data structures are simple. You spotted a Supportive domain.
With division in hand, you set about determining bounded contexts. It turns out that all 3 subdomains operate on the same objects, operate close to each other, and use the same (ubiquitous) language. You decide to wrap it to a bounded context called "Offering".
It is time to choose patterns for your subdomains:
Subscriptions - complex business logic, complex data structures. You decided to go with the Domain Model pattern.
Publishing - trivial business logic, complex data structures. You decided to go with the Active Record pattern.
Discounts - trivial/no business logic, simple data structures. You decided to go with the Transaction script pattern.
After all, you decided to go with Modular monolith. The example structure could look like the below:
Solution folder for each bounded context, e.g. Offering
Inside each bounded context, a folder for each subdomain, e.g. Subscriptions, Publishing, Discounts
Inside each subdomain folder, a set of projects (for Subscriptions: Application, Domain, Infrastructure; for Publishing: Presentation, Service, Business logic, Data access; for Discounts: Presentation, Business logic, Data access)
Then it could have been very easy to extract a subdomain (in case it is really needed) to a microservice. I omitted things like integration events etc. to distill the core of the problem.
How would you structure your solution based on the above assumptions?
I am incredibly new to Domain Models and I am trying to build up my understanding. I have created this domain model around a scenario which I will provide. I feel this model is simple and as a result, feels incorrect and might be missing elements I might not have thought of although, I cannot think of what else might need to be included in a domain model given the scenario. The idea is to demonstrate the relationship between real world class entities which I feel I have managed to achieve.
Scenario: Management Application that allows you to create users, projects, companies and issue tickets. The projects are assigned to companies, the users are assigned to projects and the issue tickets are assigned to the users. Tickets have a status which can be changed.
Changes
Implementing proposed changes. I think this is a better way to represent the idea based on the feedback returned, especially in regards to the use of composition. I have also updated the multiplicities to better represent the scenario.
Further changes
The diagram should stay as simple as possible, but not more.
In this specific case:
The two specializations of User might be too complex for the need: a User stays a User, isn’t it? If you really need to take into account differences between categories of users, and especially if the category changes over time, you'd better consider (object) composition over inheritance (or better worded for UML: prefer association over inheritance).
The associations might be too simple or incomplete. For example, before an Issue ticket gets assigned to a User, isn’t it also associated to a Project or a Company? It is not clear either if User is also associated to Company (e.g. multi-tenant cloud scenario) or if there is no such association (e.g service provider scenario, where the company is in fact a customer company).
Some associations may hide association classes, e.g. do you expect to monitor how many time a user worked on a ticket?
It entirely depends on the purpose of your model.
Some models might be created to stimulate discussion and further discovery. Some might be required for the senior stakeholders to approve. Some might be for developers to work from. Others might be for marketing material.
Your model is ok for stimulating discussion and further discovery.
I have an ERP project with multiple sub-domains. It is not using CQRS or domain events.
I have two sub-domains; CRM and Accounting. The customer concept needs to be modeled differently in the two sub-domains. CRM needs to know the size (number of employees) of the company but not the tax number. Accounting needs to know the tax number but not the size. The company name is needed by both sub-domains.
I am thinking of modeling both CRM Customer and Accounting Customer as entities. But then whenever a new customer is created by a CRM user, an Accounting Customer instance also needs to be created. And if a report needs information from both sub-domains, then the queries become more complicated then when you have single entity containing all the information.
Is this the way to go? Is there a better way? Does it make sense to have multiple sub-domains without utilizing domain events?
Are you sure you need DDD? The use case seems quite simple, maybe you just left out all the other complexities, but from just the info you're asking, a simple CRUD app would do. Data Centric apps, like reporting, don't need DDD. You need DDD when you must modify the data in strict ways, to maintain consistency.
If you are sure you do need DDD, then you need to understand the point of the model is to protect against the invariants of the domain. You say a CRM Customer must always have an equivalent Accounting Customer. How is this handled by the business today? How does accounting know about CRM customers? How does accounting know they're talking about the same customer as CRM? However they are doing it currently, is what you should try to model.
As an example, if they do it in real life by just letting the other one know. You could have your CRM context publish a new Customer event, and your Accounting context could react to it by creating an Accounting Customer for it.
If on the other hand, they both learn about it from something else, then maybe they both react to that other something's event.
If you don't want to use events, it could be a direct call, from the CRM context to the Accounting context. Though know that this would grow more restricted as the app grows, but if again you've got a simple domain, its no problem.
Also, querying data is not the same as modifying it. Queries should not use the domain model entities and value objects. It could, but it should not be constrained by it. That's because query is a read only operation. You need to put your data inside your domain model only when you are going to change it.
I recently decided to learn Domain Driven Design. I came up with a hypothetical application and tried designing the architecture for it. It is a simple Poing-Of-Sale application with bounded contexts Sales and Inventory. This is where I have two conflicting designs when implementing the code for these bounded contexts.
Design #1:
Anything and everything that has to do with inventory belongs in inventory bounded context.
If a sales order comes in, the request initially enters the sales bounded context, then one of the steps to make the sale, you must check the inventory to see if the item is available. In which case, you request the inventory context (however this is done within the system). Inventory context will check the database and respond back with availability confirmation. This way any other bounded contexts that need any kind of inventory involved logic would use this bounded context to achieve it. Code is encapsulated and good to go.
Design #2:
The division of bounded contexts are strictly at the business level divisions in their operational contexts.
If a sales order comes in and hits the sales bounded context, it should contain all the logic in code that has to do with sales. It would check the database whether an inventory is available via the service, it would then remove that item from the inventory, record the sales in database via the service again, send out an sales made notification email to admin if this is a requirement. Just everything and anything that has to do with sales will all happen in this bounded context. Once the sale is made, it could fire an event sale made, and inventory context would listen to this to check the inventory in the database, see if new purchases need to be made to bring in new inventory or not as it is an operation related to inventories at the business level.
I am just trying to understand what Domain Driven Design approach is in such system. Thanks.
=================================
After some thought this is the added question to the original.
Let's say your business needs to do shipping. Whether it would be due to making a sale (Sales Bounded Context) or due to a warranty replacement (Support Bounded Context). What if shipping itself is complicated depending on situations. Where certain products or shipping addresses you need to make the decision to ship it yourself or via some 3rd party shipping company using a web service. Does the "shipping" deserve its own Shipping Bounded Context? Or it simply is just another domain logic embedded in the Sales Bounded Context and the Support Bounded Context? This is all within the case of simple retail store domain.
My 2 cents... Design #2 seems better as Design #1 should lead you to a distributed system. You do not want a distributed system. You should not take storage or tables into account before getting the business. Just consider business, and when you get it, consider how you could be able to get your BC run in complete isolation (offline mode vs distributed mode). If data is missing, then consider using Domain Events to propagate this knowledge to your BC.
Design #1 is the correct one. Inventory context should be the only one which decides and knows how to check for inventory. It could be the inventory context is checking from multiple places and those could be changing based on meta data updates as new data warehouses come online. At some point retailer might decide to have all physical shops as data warehouses as well.
Similarly shipping should be a context as well. Notes above said we should not aim for distributed system but I don't see why not if that provides agility.
So we may apply the domain driven design for multiple projects but there could be intersection of the same piece of domain model.
In this case, how to apply the domain driven design (use ORM, model first, generating database schema)? Create multiple databases with a lot of same tables? Or how to share data? Use synonyms? What is the possible strategy to resolve the sharing model (including data)?
Any suggestion is welcome. Thanks in advance!
In my previous project we had a lot of discussions regarding having redundant info in several models that have some shared parts.
What we found interesting is that we thought that several projects (not C# proj, but real large development projects) or call it systems very rarely share the exact same perspective on how using the model. We thought that in a larger domain that spans over several application/systems/projects you could spot several core's where you don't want the cores to be duplicated in each application.
It all ended up with a domain that where distributed on several machines. And we had GUID keys to bind them together in database. But since we did this "model first", sub domains looked at each other like infrastructure related services that where reached through Domain events.
Complicated? Not really. Here's an example:
Domain one (Salary review system) - We have a Salary Review statistic system which conduct evaluation on employees salary and how they related to their experience, age and performance. The Core is questionnaire form, work evaluation, questionnaire answers, rating. salary modification advices etc.
Domain two (Employee system) - Here you manage your employee, register new employees, handle rehab, maybe personal development, salary, employee contract, employee benefits etc.
Domain three (Performance management) - Here you handle history of employee experience, goals, achievements, and agreements between boss and employees about personal development, rating and grade of performance.
As you notice the Core of each domain is different but they share some concerns. Depending on deployment, infrastructure and requirement on how tight they should relate/respond to each other - the tech how to solve this could differ.
I Prefer to do this tech independent. We used NServiceBus for synchronizing domain through Domain Events (Udi Dahn's Domain Event Pattern).
For instance, Once we have completed a salary review for an employee and boss should be informed that Joe should get a chance of salary increase with 200 - 500 $ this year.
The method ApplySalaryReview on entity aggregate root Employee do not only save the review result, it also trigger domain event NotifySalaryReviewSubscribers which trigger an eventhandler HandleNotifySalaryReviewSubscribersEvent in Application layer that takes a infrastructure service in ctor. That service puts result in a message queue that all systems that need this info can subscribe on this message.
In our case, it is Domain two (Employee system). The employee system import result and notify employee's boss that he got new info for the upcoming salary talk with this particular employee.
I hope I may have shread some light on one way of doing it. There are so many other ways as well...
You might want to (re-)read the strategic design patterns in the blue book.