Zadnjih par godina sve više proučavam DDD (Domain driven development) način “razmišljanja” o razvoju aplikacija. Pristup problemu sa stajališta business eksperta i strukturiranje klasa i namespaceova, kao i upotreba različitih objektnih design patterna, tako da budu razumljivi vanjskom konzultantu i stručnjaku iz poslovne domene a ne samo programeru, mi zvuči logičnim i pravilnim. Moglo bi se reći da mi je standardno strukturiranje aplikacije na Sučelje (Interface) – poslovna logika (BLL) – pristup podacima (DAL), te isto takvo nazivlje varijabli i objekata počelo smetati (u nedostatku boljeg objašnjenja; nešto tu smrdi a nije moj overclockirani procesor). [more]
Razmišljajući o toj tematici, i kako da takav pristup primijenim u svojoj demo aplikaciji, pokušao sam sa vlastitim nahođenjem postaviti osnovnu infrastrukturu, ali to je ispalo dosta teško jer ja ipak gledam stvar sa čiste programerske perspektive. Takav način (poznat još kao i MSDN način pošto ga Microsoft često zagovara u svojim MSDN dokumentima i kao pravilni način izrade enterprise projekata) govori o objektima ovakvih naziva: DataAccessManager, ClassLibrary, DataSet, SqlRepository i sl. Ako pogledate ta imena, dali imate ikakvog pojma čemu služi pojedina klasa i objekt? Možemo zaključiti da DataSet sadrži nekakve podatke, ali koje, zašto, kako, to ne znamo. S druge strane. čitajući članke i knjige, u DDD perspektivi imamo domenski sloj, koji sadrži funkcionalnosti bitne za poslovni model. Zatim se spominje Service sloj, Application sloj, Infrastructure sloj, i da ne nabrajam dalje. Međutim, nije li to samo promijenjeno nazivlje ali isti onaj maloprije opisano pristup sa generalizacijom problematike iz programerske perspektive?
Zato prenosim ovdje cijeli jedan članak od Jeremy Skinnera, tvorca FluentValidation frameworka za validaciju (koji će biti također upotrjebljen u demo aplikaciji!), koji po mome sudu jako dobro objašnjava što DDD zagovara, i što se u biti krije iza tog imena:
I would not consider myself a DDD expert, but here are my thoughts:
1) The purpose of an Infrastructure project
I don't think the 'Infrastructure' project is the correct place for your entities/repositories. In a DDD architecture, the focus is obviously on the core business domain as represented by the various components of the system (entities, value objects, repositories, services etc). As such, I would suggest that the Entities should be part of your 'Domain' project as these are the core objects of your system. I would suggest that you reserve your Infrastructure project for classes that perform no specific business task, but are instead only assist in building the application structure (eg a base class that all your entities use, classes necessary for your Unit of Work implementation etc)
2) Namespace usage
The structure of your application is still very programmer-oriented. That is, you've grouped objects into namespaces that make sense to you as the developer, but probably won't make sense to the business expert. One of the core points of DDD is that there should be a ubiquitous language used by both the developer and the business expert to discuss the domain, and that language should manifest itself in the software. As such, rather than grouping your classes into namespaces like 'Entities' and 'Repositories', I would suggest that you instead group them by business area. For example, you might have an 'OrderProcessing' namespace which contains the Order entity (and any related value objects), an OrderRepository and an OrderProcessingService etc. This way your classes are grouped by domain function, rather than by an arbitrary definition that only makes sense to developers.
3) The 'Application' project seems superfluous and unnecessary
The 'Application' project seems like a mesh of classes that would be better suited to living in other projects:
3a) I think you need to make a distinction between the different types of services, ie 'Domain' services and 'Infrastructure' services. Domain services (eg an OrderProcessingService, or a TaxCalculationService) perform task that are core to the business, and as such should live in the Domain project in an appropriate namespace (see point #2). Services like a FileUploadService are not performing a business task, so they would probably best suited to live in an Infrastructure project.
3b) You mentioned that you were going to "Define new Entities that are more adapted to the Views" as part of the Application project. I don't really see the need for this. AccountLogin, AccountSignIn, AccountEditProfile etc all sound like they're View Models - not entities. Entities are a software representation of the core concepts of your domain, so in an order processing system these would be 'Customer', 'Order', 'Product' etc, but not AccountLogin. I would recommend that you think carefully about the purposes of these objects. What sort of logic does AccountLogin contain?
- If the logic is core to the domain, any only affects a single aggregate then it should be part of that aggregate. For example, if the 'User' object is the root of an aggregate containing 'User', 'Role' and 'Profile' then any logic that effects these classes should probably belong to the 'User' object. It would probably be a a good idea to have 'ChangePassword' or 'AddUserToRole' methods on the 'User' object than to have this in a separate class.
- If the logic touches *multiple* aggregates, then it should probably be in a separate Service. A contrived example: when a Customer pays for an Order, you want to 1) mark the order as successfully processed and 2) update the 'DateLastOrdered' property on the Customer. As this operation touches two different aggregate roots (an Order and a Customer), then it may be appropriate to put this in a separate service class.
- If the logic is only related to the UI and not to the domain, then that logic should be part of a View Model.
For example, what is the difference between 'AccountSignIn' and 'AccountSignInViewModel'? To me, it sounds like they should be the same, ie a projected representation of the User aggregate for the purposes of signining into the system via a login screen. This screams "View model!" to me :)
3c) Having view models is definitely a good idea, but I think it would be better to store them in the UI (Presentation) project as they're only going to be used by the UI.
4) Validation.
I honestly don't think that FluentValidation is appropriate for validating Entities in a DDD scenario. Here's why:
As you know, FluentValidation works by validating the properties on an already populated object. It checks the value of each property to see if it is valid given a set of rules. This means that the object can exist in an invalid state until you perform validation. However, an entity should always be an accurate representation of a concept in your domain, and as such it should *never* exist in an invalid state. As such, you should build any necessary validation into your entities' properties. For example, if a Customer object has a Name property that is not allowed to be null, then you should put a null-check in the setter for the Name property. This way your entities cannot exist in an invalid state.
FluentValidation is far more suitable for validating your view models (in fact, this is what it was designed for). A view model is merely a representation of data sent to a view, or received back from a view. So let's say that you have a form for editing a customer's address. The values of this form could be posted back to your controller and deserialized into an AddressViewModel using a model binder. At this point, the AddressViewModel *could* be in an invalid state, so this is where FV comes in with a AddressViewModelValidator. If validation succeeds, you would then map the AddressViewModel back into the appropriate properties of your Customer object (either manually or by using something like automapper).
5) Unit of Work
My experience with the entity framework is limited, but I don't see why this shouldn't work with .NET 3.5 SP1. By its very definition, an ObjectContext *is* a unit of work. That is, it tracks the changes made to any entities within its scope and then you can either commit all the changes or discard them. In an ASP.NET MVC application, I would tend to have an ActionFilter that is applied to all of my controllers. At the start of the http request, the filter would create a new ObjectContext and store it in the HttpContext.Items collection. You would then use this same ObjectContext for any database operations in that take place during the current http request. At the end of the request, the action filter would submit any changes that have been made (but only if there are no validation errors).
As I said, I'm not a DDD expert, so these are just my opinions based on what (little) I know about DDD. If there's anything that doesn't make sense, or anything you want me to clarify, please let me know.
Jeremy
Cijeli thread rasprave možete pročitati ovdje
Slijedeći tjedan ću postaviti osnovnu strukturu projekta i opisati zadaću svakog njegovog dijela, naravno s pokušajem primjene DDD načela. Također, trebao bi napraviti jedan mindmap svih featura, da bi odabrali one bitne za dobiti prvu verziju web sitea, i prema odabranima featurima formirati taskove. Svaki task ne bi smio trajati vise od par radnih sati, ipak se tu radi o relativno malenom projektu.
Nakon svake iteracije izvršenih taskova po jedna od funkcionalnosti web-a bi trebala biti napravljena i odmah vidljiva – znači ne raditi cijeli data access layer, pa onda cijeli interface, itd. Tim načinom bi prve rezultate vidjeli tek kada bi natipkali 80% html i c# koda. Ovakvim agilnim pristupom ćemo puno prije vidjeti rezultate, i lakše intervenirati kasnije kada uvidimo određene probleme i zamjerke (lakši refactoring).
Također, koliko god budem mogao pisati ću i Unit Testove, iako za sada neću ići sa red-green test-first pristupom (prvo pisanje testova koji ne prolaze, potom pisanje koda i testovi prolaze)