views:

157

answers:

7

Hi all

I'm reading through Eric Evans' awesome work, Domain-Driven Design. However, I can't help feeling that the 'layers' model is contrived. To expand on that statement, it seems as if it tries to shoe-horn various concepts into a specific, neat model, that of layers talking to each other. It seems to me that the layers model is too simplified to actually capture the way that (good) software works.

To expand further: Evans says:

"Partition a complex program into layers. Develop a design within each layer that is cohesive and that depends only on the layers below. Follow standard architectural patterns to provide loose coupling to the layers above."

Maybe I'm misunderstanding what 'depends' means, but as far as I can see, it can either mean a) Class X (in the UI for example) has a reference to a concrete class Y (in the main application) or b) Class X has a reference to a class Y-ish object providing class Y-ish services (ie a reference held as an interface).

If it means (a), then this is clearly a bad thing, since it defeats re-using the UI as a front-end to some other application that provides Y-ish functionality. But if it means (b), then how is the UI any more dependent on the application, than the application is dependent on the UI? Both are decoupled from each other as much as they can be while still talking to each other.

Evans' layer model of dependencies going one way seems too neat.

First, isn't it more accurate to say that each area of the design provides a module that is pretty much an island to itself, and that ideally all communication is through interfaces, in a contract-driven/responsibility-driven paradigm? (ie, the 'dependency only on lower layers' is contrived). Likewise with the domain layer talking to the database - the domain layer is as decoupled (through DAO etc) from the database as the database is from the domain layer. Neither is dependent on the other, both can be swapped out.

Second, the idea of a conceptual straight line (as in from one layer to the next) is artificial - isn't there more a network of intercommunicating but separate modules, including external services, utility services and so on, branching off at different angles?

Thanks all - hoping that your responses can clarify my understanding on this..

Edit: In fact Evans clarifies later that it is case (b) he is talking about: "Layers are meant to be loosely coupled, with design dependencies in only one direction. Upper layers can use or manipulate elements of lower ones straightforwardly by calling their public interfaces, holding references to them. ... When an object needs to communicate upward we need another mechanism .. such as OBSERVERS"

This seems wholly contrived to me. It seems to confuse design dependencies with just the flow of the interaction. UI needs a reference to the app because actions are generally user initiated! For actions that are app initiated, eg some internal timer event alerting the user to something, the app now needs a reference to the UI, just as much as the UI needed a reference to the app earlier. In what sense is one more 'design dependent' than the other? To back up my point, to implement OBSERVER, it's the APP that's going to need a list of references to the UI components.

It seems to me that the only reason the references and design SEEM to go from top layer to bottom is because that's where the actions typically get initiated. When the action is initiated in the app, the references have to go the other way..

+2  A: 

I agree with you. The ideas in DDD sound really cool but very hard to implement. Also note that for a simple CRUD app with two or three forms, Layering of the App itself could be a major overkill.

However note the problem that Layer Architecture in DDD is trying to solve.

In an object-oriented program, UI, database, and other support code often gets written directly into the business objects. Additional business logic is embedded in the behavior of UI widgets and database scripts. This happens because it is the easiest way to make things work, in the short run.

When the domain-related code is diffused through such a large amount of other code, it becomes extremely difficult to see and to reason about. Superficial changes to the UI can actually change business logic. To change a business rule may require meticulous tracing of UI code, database code, or other program elements. Implementing coherent, model-driven objects becomes impractical. Automated testing is awkward. With all the technologies and logic involved in each activity, a program must be kept very simple or it becomes impossible to understand.

Notice this diagram from Microsoft which depicts a very good implementation of layered architecture using DDD. Notice the notion of UI Views.

alt text

CodeToGlory
A: 

I think he means that the "lower levels" are independent from the "higher levels" in as much that they can stand on their own.

It makes no sense to have a UI layer without a servive layer being present. But it makes perfect sense to have a service layer without a UI layer if the services are exposed as webservices or RPC endpoints.

Similar for the Repository or DAO layers.

I agree with your point that if these are well decoupled you can swap out service layers from under the UI (as is being done with flatfile "service layer" mockups to create UI's before a letter of code of the service layer is written) or Repositories (often swapped out in integration/unit testing of the service layer etc.

But the dependency of having the lower layers being present for higher layers to be functioning is a conceptual one-way dependency.

Peter Tillemans
Thanks Peter. I like the direction your answer is taking, but I don't understand what 'stand on their own' means. If I experiment with it meaning "Makes no sense by itself", let's see how that applies to the different layers in order.A UI doesn't make sense by itself (but does when coupled with lower layers). Yes, agreeService layer doesn't make sense by itself (but does if coupled with lower layers). Yes, agreeDomain layer doesn't make sense by itself. Well - it really doesPersistence layer - doesn't make sense by itself OR with lower layers as there are none.So I'm still confused..
Jeremy
A: 

I don't think layering and decoupling modules from each other by using interfaces are mutually exclusive, which is how I interpret your post.

In my world, layers coincide both with the scope of functionality and compile/runtime dependencies. A layer on top of another layer uses and adds some kind of value (from a user's perspective) to the one below it - which often consists of multiple "island-ish" modules that accomplish a well defined function. I always hide the implementation behind an interface, easing the pain of changing it. This also gives me the ability to compile/build my code without implementing every layer, which in turn makes it easier for multiple people to work together on it.

Here's a nice diagram that applies to most of the serious stuff I've build:

UI/API controllers
       |
    A Facade
       |
Composite Services
       |
Isolated Services <--> External Services
       |
      DAOs
       |
  Persistance
Lauri Lehtinen
A: 

I've seen the idea of layers be used very effectively, and I've seen apps where it was needed but not used.

For an example of the first case. I worked on a project where we built the infrastructure and services for service-oriented architecture. (We did this because it was 1997 and you couldn't just go download the pieces then). This example built one layer on top of another. There was a network layer, a protocol layer (using XDR for portability), etc. In a way, it was similar to the layering within TCP - note how the sender adds to the transmitted unit in the reverse order that the receiver peels them off. A key part of the reason that this worked was that the lower layers could be thoroughly demonstrated to be rock-solid and re-used in various places. Most bugs didn't occur in these layers, and we didn't have to worry about them. That reduced debugging time considerably.

For an example of the latter - I worked on another network application built on top of IRC. Sadly, in this case, there was no layering. The code was very reminiscent of spaghetti code that I saw in my early years. In that project, there was a User object that was used all the way down to the network layer. It was a disaster. Certain bugs in the User object could break things all the way down to where the code was putting stuff on the wire. I've seen cases where I've had to throw code away and start over. I wanted to do it in this case, but the political landscape prevented it.

Is layering right for every app? Probably not. But when you're building an app of any amount of complexity, you should have it. It's one mechanism that helps us adhere to the rule of Separation of Concerns.

Don Branson
A: 

It's not necessarily contrived, but it is very difficult to achieve in practice.

Given your example of the UI and the domain layer: The domain objects purpose is only to store information pertaining to the problem being solved, and expose methods and properties for modifying that information as well as notifications of when changes have been made. This requires zero coupling to the upper layer - the UI.

You point out that "this is clearly a bad thing, since it defeats re-using the UI as a front-end to some other application that provides Y-ish functionality." Generalizing a UI such that it can accommodate myriad domain models with similar functionality/intent is not a design goal that I'm familiar with and I don't believe it's a design goal in DDD either. I suppose a pattern like MVC could allow it, but that just moves the problem to the controller.

It gets a bit dicy the further down you go though. If you're using POXOs (Plain Old [Insert Language] Objects) then your persistence framework, at some point, will be need dependencies both above and below it.(1)

Most of the code however can maintain a downward-only dependency graph until you approach the bottom.

(1) ORMs know about your domain model + the DB if you're using DTOs then it knows about the DTOs and the db, etc.

SnOrfus
"Generalizing a UI such that it can accommodate myriad domain models with similar functionality/intent is not a design goal that I'm familiar with and I don't believe it's a design goal in DDD either" - I would put web browsers in this category - they can connect to multiple back-ends, which all implement http request/response, but no web-browser is coupled to one particular web site. This is why I think a downward-only dependency graph Evans describes has an appealing neatness to it and is easy to diagram, but doesn't actually represent accurately the real dependencies.
Jeremy
+1  A: 

You are hinting at the Dependency inversion principle:

A. High-level modules should not depend on low-level modules. Both should depend on abstractions. B. Abstractions should not depend upon details. Details should depend upon abstractions.

Part A implies that no partition of your application is directly dependent on the implementation details of another.

Bryan Watts
Arnis L.
+1  A: 

Eric's description of layered architecture is indeed somewhat confusing. As I understand him, he pointed that good application have some layers and these layers should be decoupled. The sole subject of layers is worth is own book and is not described further (beside the small fragment you've quoted) in the book.

In fact DDD does not depend on layered architecture, but I bet all real-world implementations of DDD do use layers in architecture. A better (than Evans) explanation on architecting DDD-based solutions it the one from Jeffrey Palermo: the onion architecture. Palermo talks in details about responsibilities of each layer and about paths of communication. I personally use this approach with much success in my own projects. If you want to see how this can look in practice, take a look at DDDSample (if you are Java guy) or DDDSample.Net (the .NET port of DDDSample I've written)

Szymon Pobiega
Awesome answer - thank you! You've got to the heart of what was bothering me about it, and the onion architecture in the Palermo article looks a lot closer to a representative model. In fact I'm surprised that Evans' model doesn't involve having the domain model as the central piece, given the rest of the book.I will take a look at your sample code - thank you!
Jeremy