Domain Model – Repositories – Communication across Sub-Systems
I am currently in the process of designing a system which will use multiple data sources to consume the required data. I am attempting to model the concepts shown below (would post an image but don't have enough points!) where a customer can have an association with a number of products. The Customer would be stored in the "Customer subsystem" and the Product and CustomerProduct would be stored in thee "Product subsystem"
public class Customer
{
public string FirstName { get; set; }
public Guid ID { get; set; }
}
public class CustomerProduct
{
public Guid ID { get; set; }
public Customer Customer { get; set; }
public Product Product { get; set; }
}
public class Product
{
public string Name { get; set; }
public double Price { get; set; }
public Guid ID { get; set; }
}
The “Customer” entity will be physically persisted in a system which must be accessed via a web-service. The “ConsumerProduct” and “Product” entities will be persisted in a SQL database, using NHibernate as the ORM.
As part of the design I was planning to use Repositories to abstract the data persistence technologies away from the domain model. Therefore I would have 3 repository interfaces, ICustomerRepository, ICustomerProductRepository and IProductRepository. I would then create a concrete NHibernate implementation for the CustomerProduct and Product repositories and a concrete web service access implementation for the Customer repository.
What I am struggling with is how the entities, which are persisted in different sub-systems will interact. Ideally I would like a rich domain model, where the CustomerProduct entity would have a physical “Customer” property which returns a Customer object. However I have no idea how this would work as the Customer entity would need to be accessed from a different data store.
The only way I can see to solve this issue is to not maintain a full reference to Customer in the CustomerProduct entity and instead just hold a reference, and then every ti开发者_如何转开发me I need to get a reference to the Customer I would just go via the Customer Repository.
I would be grateful for any suggestions anyone could put forward on how to solve this issue.
hi I haven't been in your situation before, but I have designing domains that communicate with other subsystems. I do not have the whole picture, but it seems like the customer entity is more isolated from the others, CustomerProduct and Product. So am I guessing correct that you will present the model in a common GUI but its only the datasource that are separated?
First you can solve this by different ways and you should also ask yourself about non-functional requirements such as maintenance, uptime and support. Will both systems always be up and running simultaneously or will it happened that you take on system down. The clue I'm fishing for is should you communicate sync or async (message queuing?) with subsystems. This can be achieved by using NServiceBus.
But to focus on your Domain, you should go for making the Domain look like it only has one model. This can be accomplished in different ways:
1) Have your ICustomerRepository (an interface contract that acts like is working against a collection of objects) be implemented by a infrastructure related repository that consume your web service in your subsystem. A hint is that you should use GUID as keys so keyconfilcts occur. This approach will not let you have any relationships/associations to customer from your other entities. They will but only through the repository (This a solution that Jimmy Nilsson uses in his book (http://jimmynilsson.com/blog/) to not tighten the model with to many bidirectional relationships).
2) Depends how your use cases will target/use the model, but you can create a application wide service layer that resides at one physical place but uses CustomerService and CustomerProcuctService and ProductService. To prevent that domain logic will leak into application layer some of the coordination between these entites can be encapsulated in a domain event handlers that coordinate some events between different services.
3) you can also create a CustomerAdapter class that have the other subsystems CustomerGUID as a key (it cannot generate keys since Customer webservice have control of that). But you can map it in Nhibernate and have relationsship between CustomerProduct and CustomerAdapter. But when you Map CustomerAdapter you will only load the GUID. Then make sure you will have a ICustomerAdapterService injected into a property using Spring.Net Windsor or some other DI tool. Then you to not map properties (like customername, adress etc) for customerAdapter in Nhibernate. But when you get/read Adress from CustomerAdapter it will get it from ICustomerAdapterService and set all other values as well. This is not a recommended solution since it will break some DDD rules, like not having services in domain model. But if you see it from this perspective: it actually can be considered a Domain Service since it solves problem within your distributed domain. However it includes infrastructure related things like a WCF service implementation and therefore should the service implementation be in another infrastructure layer/assembly
Most simple is the solution 2 If you can handle the fact that Customer Entity will be accessed only by a Service in application layer. However this application serviclayer can be a good anticorruption layer between the two subsystems. There is probably a reason why you have two subsystems today. But an example of interaction flow (with no detailed knowledge of how your domain is):
- GUI calls Application Service CustomerProductService method BuyNewProduct(CustomerDTO customer, ProductDTO newProduct)
- CustomerProductService have ICustomerProductRepository and IProductRepository injected into constructor. It will also have a infrastructure Service ICustomerFacadeService (change name now :-)) that is injected into a Property CustomerFacadeService. The creation of this service is done by a factory that have two creation methods, Create() and CreateExtendendedWithCustomerService(). The later one will also inject CustomerServiceFacade
- The method BuyNewProduct(...) will now Assemble the CustomerDTO and use the CustomerGUID to load Customer from CustomerFacadeService that will call the web service in the other subsystem.
- The loaded customer will ensure that it actually exists but now we load the Product with IProductRepository
- With both CustomerGUID value and Product Entity we create a new CustomerProduct entity (which is actually just a mapping class between Products and Customer GUID's) and save it through ICustomerProductRepository
- Now you can call another infrastructure service to send an email to your customer that will be notified that it has access to the new product. Or you can create Domain events in CustomerProduct entity that delegates this notification to en eventhandler (in application service layer) that has the IEmailService injected in ctor. Then you have incapsulted the domain knowledge of sending notifications when you connect a new customer to a product.
Hope this help you in modelling your domain with less pain. Because its painful to do DDD. Requires a lot discussions with colleagues, domain experts and yourself in front of the mirror :) Is this the right path? Look at the DDDsample.net for Domain Events or search for Udi Dahan and domain events.
I write an answer here, more space:
Regarding CustomerAdadpter also refered as CustomerFacadeService in the interaction flow example here is my opinion: How to implement depends of your application. Will most usercase be calling mainsystem calling your "cloud-subsystem" which will have a good uptime: -Then you maybe do not need a queue and will have a WCF service in the cloud. Your CustomerFacadeService will be a service Wrapper that just exposes the method your application layer needs and also assemble all necessary DTO objects. If your cloud system will also call back to your mainsystem then you need to expose some of your methods as a service. THEN you have the option to expose a NServiceBus endpoint as a WCF service. This gives you the possibility to take down the mainsystem without loosing information. But there is always a lot of buts... You need of course to have the WCF service on another machine if your infra-tech guys want to install hotfixes/reboot main system's web server. If you have client's waiting for an response while main system is down, how long will they wait? Not too long I guess. So one scenario I can see benefits of this is if you have batches/reports that need to be carried out, and if one part of the system is down, the reporting will continue once it's up again.
Here is some example of NServiceBus exposed as a WCF service. But I have no experience in doing exactly that, just the knowledge of "it can be done". http://docs.particular.net/nservicebus/architecture/nservicebus-and-wcf
精彩评论