Working to reduce connectivity and thinking about SOA came to the idea of constructing types by composing interfaces.
In classic DDD, you need to select a domain - a collection of objects and their connections. But when I applied this principle in my life, I encountered two difficulties:
- If there is a large domain and a bunch of services around it, then it becomes difficult to control access to class members. It looks like this: there is a User object with the CreatedAt property, which I want to allow only the MembershipService service to edit. Well, we write InternalsVisibleTo with the indication MembershipService. Next we need to make an object, for example Car, which has a PassedDistance property, which I want to open only for the CarService. Again we repeat the manipulations with InternalsVisibleTo, but here a problem appears: now Membersip can change the mileage of the car, and CarService - the date of registration of the user.
- DDD still does not allow building an application “brick by brick” - simply by connecting the necessary modules. They say that Ruby is possible, so I want the same ease in .Net =). But it doesn’t work out for the same reason - the domain is selected in a separate assembly, and when connecting the service, you have to manually pull all dependent entities from the domain, sorting through their properties, since most of them are not needed in the new project. Those. the problem of connectedness of services DDD solves, but the connectedness of the domain is still not solved.
')
One way out
- merge the service and its entities into one module. There will be two assemblies (domain and service) or one single assembly - it does not matter. The goal is to connect all the functionality of the module simply by adding reference to the assembly in which the application composition is executed (AppCore, for example).
Having done this, you need to somehow leave the possibility of communication between the domains of different modules. It is likely that in our example, you will need to implement the Car.Owner property of type User, and then House.Owner, CableTv.User, etc. To add a reference to Membership is not an option, it already contradicts the main idea - to prevent the modules from being connected.
Solution ideas
Select all such dependencies in the assembly, but not with whole entities, but with dummy interfaces. In this example, it will be an empty IUser interface. Then, during the initialization of each module (in its Bootstrapper) to expand this interface with what is specified inside the module (ICarOwner), in which you can specify any properties that are required for User within the framework of this module.
Thus, each module complements the IUser interface with its own fields. Then you need to implement a factory that, based on the list of interfaces, will create a single type of User, which will be cached in AppCore.
Advantages and disadvantages:
+ Each module only knows about the fields it needs.
+ Except for the basic interfaces, the modules will be completely independent.
+ When adding a new module, the necessary properties for the persistence will be added automatically. Those. when you add a module, the Sql-diagram is automatically replenished with the necessary tables, columns and other objects.
- The idea has not yet been implemented =). But for someone who wants to implement this approach, it will be a plus - he will be the first to implement it.
The goal is to make Plug & Play functionality.
Orders are needed - added a reference to OrdersLib, launched it, that's it!
The database is automatically updated with new tables, columns, links that are required for the work OrdersLib. The User will automatically have User.Orders, etc.
No work on the adaptation of the code, only the extension where necessary.
How to figure out what the User object consists of? There are two options:
- code generation of a common interface, which will even simplify the creation of a factory
- development in the environment of the developed application. Intellisense will not read the bytecode, but the real instance of the dynamic object.
The first way will keep strict typification, the second - will make development completely dynamic.