문제

I'm making an application using DDD principles. After thinking everything through as much as I can I'm down to start making my Bounded Contexts. I haven't set the final structure yet, but as of now my application will consist of the following bounded contexts:

  1. Employee managment
  2. Purchasing
  3. Archive
  4. Reporting

I want this to be as pluggable as possible, so I can for example develop and maintain them seperately. Probably they will expose either WCF or Web API to interact with them.

I will use Udi Dahans implementation of a simple CQRS pattern. I'd prefer not to go all the way with event sourcing, message busses and so on because this wil not be a highly collaborative app (less than 1000 users, and they are not likely to edit the same small dataset), and this would add a lot of unneeded complexity.

So to the questions:

The employee and department entities are common for all BC, how to model that?

Department is a part of the organizational structure, so in the employee managment BC the employees work in a department, they can manage an department and they have a history of departments they've worked on.

In the purchasing BC goods are purchased from a department, and delivered to a department. Suplliers have different contracts with different departments.

In archive some information will be archived and tied to a department, and so on.

The same pretty much applies to Employees.

How to persist the data from the bounded contexts?

They can be mapped to the same database or each have their own.

Some thoughts I've made so far

How to model
Should I make one more BC called "Company" or "Organization" and manage departments there?

Acoording to Udi Dahans article referenced above, I should make a department entity and a employee entity for each BC with just the fields and behavior I need for that BC. This sounds reasonable but then I'm thinking of how to actually use this and I can't figure that out. I need to access the departments managed somwhere else, but how exactly do i do this and not mix my BC's?

How to use?
Let's say that I get my list of departments from somwhere by querying. In the UI i get a list of departments that I want to make a purchase too. This is the first purchase for this department so the Purchasing BC does not know about this department yet... So the Department Object in Purchasing BC will be filled with data maintained from anoher BC - so how do I persist this? I would need to add some information like delivery address, and invoice address if that doesn't exist?

In the "register department UI" should I then call "RegisterDepartment" service on all BC and then make shure that these are in sync with all changes made through the UI (MVC controller)?

The same with employees. I want to know which employee made a purchase or put something in the archive. So somehow I would need an employee-object in these BC's too but manage them from a different BC.

Persisting
Some of the challanges above could be solved by mapping the different employee-objects to the same table in a database. Purchasing BC and Archive BC cannot register new employees, but append information to those who are there and tie them to other objects in the same database. Then the database would make shure that all the BC's still live in the same world...

I need advice so I don't end up making something that will be very hard to maintain later on.

도움이 되었습니까?

해결책

It seems that most of your doubts circle around: "How are single real-life objects shared by different Bounded Contexts?"

The thing is, though the entities are the same, they are treated differently by every BC. In Employee Management BC, the whole weight is centred on Employee and Department entities - you should be able to add them, modify, assign to each other, keep history and take care of all business logic regarding management. You could implement some policies of keeping employees personal data, maintaining proper official structure or maintaining certain responsibilities.

On the other hand, the department entity in the Purchase context would mean only, for example, invoice address and maybe person from the department responsible, and the centre of interest would be constructing the order. All data that is not directly connected to the procedure of making the purchase should be given to a different context. If, for example, the domain requires that every order has to be connected to the department and the invoice details are missing, purchase context should not try to fill them by itself. Instead, a notification to Employee Management could be made to fill the missing parts.

Note that it may very well happen in the same application or even the same window. But you must ensure it will happen through Employee Management context, i.e by invoking context public API.

As a side note, I do not know your domain, but you may want to reconsider your context boundaries, for example by separating deliveries from purchase.

Moving on to usage and following your example, if you want to make a purchase, I would consider following path:

  • Read necessary department data (let's wait till later with "how"), you may want to check if every data are present at this point
  • Read goods that can be purchased, depending on your domain it might be worth to introduce another BC, such as Suppliers. These all above are the "Query" part of CQRS
  • Construct order or any other necessary purchase context entity, perform validation or any other logic
  • Commit changes, persist purchase context entities (the "Command" part)
  • Create and publish some domain events (for example to notify Archive or Reports)

Last but not least, you should not be concerned with global persistence in the domain layer. Every BC should be connected to some Data Access or Infrastructure layer supplying necessary objects and taking care of such details as from where to take them.

Especially, entities do not necessarily need to mirror database layout, and the question whether to store in one or multiple databases should be only a performance issue. For example, some entities will refer to the same object (for example employee Name), but can take other details from totally different tables or db (i.e. purchase history or elements sent to archive). You may use something like NHibernate to make this easily manageable.

다른 팁

I'm answering an old question here, but I have another example of the issue faced in the OPs question.

I have a situation where I am working on an application used within a manufacturing company. This company has departments for Sales, Operations, Production, Accounts, Tech Support. Within all of these departments there is a concept of Customer. So I had a hard time working out how I have a Customer entity in these departments (which I have mapped out as my Bound Contexts).

It was when thinking about this over and over that something clicked, and then I remembered a comment I read in a blog post from Jimmy Bogard, where he talked about a domain model he had worked on which took weeks of work to arrive at because the more they worked on the problem, the more they understood the domain and were able to arrive at an elegant design.

My moment of clarity was when I stopped thinking about persistence and stopped thinking about customers in these BCs.

What I realised was the Sales BC needs to know about Customers, but they need very specific information like who is the MD, a list of contacts, a list of offices, who the decision makers are, who is the finance contact etc.

Now, the Ops BC is responsible for raising an order in the system, and they need a concept of customer. However, they don't need to know who the MD is, or a list of contacts, and who cares about what offices that customer has? Ops just need to know the customer name, and in our case a 4 character customer code. When I think that through, I don't even need to persist this information as an Entity, it could just be a Value Object in my Ops BC. But how do I get this information into my Ops BC? Well, that's really simple. The BC defines an interface through which my application can interact with the Ops BC. My application has no idea what goes on inside that Ops BC, but it does know that there is an Order Aggregatr root, and that has a method to RaiseNewOrder which takes in an argument of type CustomerValueObject. The CustomerValueObject consists of a 4 letter code and a Customer Name. So, in my App UI I can use the Sales (let's call it Customer Management) BC to to get a list of customers to present a drop down list. Then, when I handle the transactional part of the operation, i.e. the user posting the new order information I use the Ops BC and pass in the customerVO as an argument.

So, my Ops BC and Sales BC are separate, and self contained, they have the concepts internally which they need to ensure the integrity of the domain, and I am able to access the data I need in my UI so that the operator can pick a customer to then perform an action in the Ops BC.

What I realised was a Customer to the sales team is not the same thing as a Customer to the Ops team. To the Ops guys, a customer is just something they tag an order with. They don't have any interest in the inner workings of that company, or any of the information about it. As long as I have a common ID for customers throughout the BCs I have a way of pulling that data back id I need to for presentation. For example, I can use the Ops BC to pull back a list of orders for customer XXXX. I don't need to go in via the Customer Aggregate to do that.

Now, throw in this wrinkle. The Sales guys will need at some point to know about orders a customer has placed, so do I need to start replicating information in my Sales BC? This is where I got stuck, but then I realised I am talking about the sales guys, they are users of the UI, and in the UI I can pull back a list of orders for a given customer.

I am still fairly new to DDD but what I am realising is DDD is all about the conversations with the domain experts, and when you have those conversations properly you realise that an Entity that you think exists across BCs... doesn't.

I think we as developers through experience are set up to denormalise data, and think about database structures, and how we will persist these Customer objects back to a table... and so we see the entity as one glob of stuff, but the domain experts do not see these entities in this way.

The comment which sealed the deal for me was from the rather cantankerous Ops Director when I asked a question like "so what would the accounts department need to see from this order" and his response, minus the swearing was "I don't care what they want, I just want what I want". And I think that's how you need to look at the BCs.

I hope this waffle helps someone anyway.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top