2

Something on my mind about structuring a system at a high level.

Let's say you have a system with the following layers:

  • UI
  • Service Layer
  • Domain Model
  • Data Access

The service layer is used to populate a graph of objects in the domain model. In an attempt to avoid coupling, the domain model will be not be persistence aware and will not have any dependencies on any data access layer.

However, using this approach how would one object in the domain model be able to call other objects without being able to load them with persistence, thus coupling everything together - which I'd be trying to avoid.

e.g. an Order Object would need to check an Inventory object and would obviously need to tell the Inventory object to load in some way, or populate it somehow.

Any thoughts?

Mark 909
  • 1,825
  • 1
  • 18
  • 27
  • can you please explain this line more "how would one object in the domain model be able to call other objects without being able to load them with persistence?" , what r u actually trying to achieve in simple form. – TalentTuner Nov 29 '10 at 17:44

9 Answers9

4

You could inject any dependencies from the service layer, including populated object graphs.

I would also add that a repository can be a dependency - if you have declared an interface for the repository, you can code to it without adding any coupling.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • OK, but I'm calling a method on one Domain Model object from the Service Layer. I don't necessarily know what I'm going to need at this point – Mark 909 Nov 29 '10 at 17:52
  • @Mark 909 - A repository can also be seen as a dependency. See updated answer. – Oded Nov 29 '10 at 18:01
  • +1. @Mark 909, this might help you get your head around using Dependency Injection if it's still not too clear: http://www.morphological.geek.nz/blogs/viewpost/Peruse+Muse+Infuse/An+Introduction+to+Dependency+Inversion.aspx Also, it depends what you'd call a Service Layer; in most cases working against a plain old interface works well. – Adrian K Nov 29 '10 at 21:20
  • I understand the principles of DI but my main question is, should the Domain Model be doing anything related to persistance at all? – Mark 909 Nov 30 '10 at 09:20
  • 1
    @Mark 909 - It really depends on the model. In DDD, the Domain is persistence ignorant, so it shouldn't know about _how_ things get persisted. That does not mean it shouldn't know about persisting (just not _how_). – Oded Nov 30 '10 at 09:34
  • So, you would pass an interface in on the constructor of a DM object and use this to load things as required? How could (or would?) you use a O/RM in this approach? And how would this related to Unit of Work. Would the service layer control when the Commit happens? – Mark 909 Nov 30 '10 at 09:39
  • 1
    1. Yes, 2. The concrete classes of the repository can use an ORM, I don't really see the issue here. 3. The UoW can be either orchestrated by the DM (if you are using DDD and aggregate roots), or by the service layer if needed. – Oded Nov 30 '10 at 10:51
  • 2. So would you pass in a global DataContext object to each, if say using LINQ to SQL? – Mark 909 Nov 30 '10 at 14:18
  • 1
    @Mark 909 - no, as that exposes the fact that you are using Linq2SQL, making your code tightly coupled to it. Create an interface with the required methods, where the implementer _uses_ the `DataContext` but does not expose it. – Oded Nov 30 '10 at 14:23
  • So you'd pass in an DAL Interface, that exposes Commit / Rollback methods for UoW and then call these. You could then pass in the Data Context in the contructor of the DAL? That would mean one DataContext object per Service Layer Method? – Mark 909 Nov 30 '10 at 15:59
  • @Mark 909 - With the DAL you could use any kind of DI (Constructor/Property/Method/Your own) to ensure it has the right DataContext, that would indeed let you have a single DataContext per Service Layer Method. – Oded Nov 30 '10 at 16:48
2

One way of doing this is to have a mapping layer between the Data Layer and the domain model.

Have a look at the mapping, repository and facade patterns.

The basic idea is that on one side you have data access objects and on the other you have domain objects.

Shiraz Bhaiji
  • 64,065
  • 34
  • 143
  • 252
  • OK - would it be the Service Layer that would call methods relating to Mapping? – Mark 909 Nov 30 '10 at 09:22
  • Yes, you can place all mapping logic in one dll, then call it as the request comes into the service layer, and as the response is on the way out. – Shiraz Bhaiji Nov 30 '10 at 10:15
1

To decouple you have to: "Program to an 'interface', not an 'implementation'." (Gang of Four 1995:18)

Here are some links on the subject:

Gamma interview on patterns

Random blog article

Googling for "Program to an interface, not an implementation" will yield many useful resources.

Paul Sasik
  • 79,492
  • 20
  • 149
  • 189
  • So you'd pass a repository interface to your Domain Model objects? – Mark 909 Nov 29 '10 at 17:51
  • @Mark: Yes, that's just for starters. Btw, that idea nicely fits with Oded's answer about DI. And decoupling doesn't just happen between layers... – Paul Sasik Nov 29 '10 at 18:14
1

You should take a look at Martin Fowler's Repository and UnitOfWork patterns to use interfaces in your system

sebagomez
  • 9,501
  • 7
  • 51
  • 89
1

Have the domain model layer define interfaces for the methods you'll need to call, and POCOs for the objects that need to be returned by those methods. The data layer can then implement those interfaces by pulling data out of your data store and mapping it into the domain model POCOs.

Any domain-level class that requires a particular data-access service can just depend on the interface via constructor arguments. Then you can leverage a dependency-injection framework to build the dependency graph and provide the correct implementations of your interfaces wherever they are required.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • So you would have the Domain Level class responsible for loading its own data as required (through calling DAL interface methods pass in) – Mark 909 Nov 30 '10 at 09:29
  • @Mark 909: The Domain Level classes consist of two types: Business Processes and Business Entities. The Business Process classes should depend on the repository interfaces, and load whatever data they need from those interfaces in order to perform the processes they are tasked with performing. The Business Entities are POCOs, and contain no logic: only data. Note that by defining the repository interface in the Domain Layer, you make it possible to use it both in your business process classes and in your service classes, as mentioned by @Oded. – StriplingWarrior Nov 30 '10 at 16:46
1

Before writing tons of code in order to separate everything you might want to ask yourself a few questions:

  1. Is the Domain Model truly separate from the DAL? And yes, I'm serious and you should think about this because it is exceedingly rare for an RDBMS to actually be swapped out in favor of a different one for an existing project. Quite frankly it is much more common for the language the app was written in to be replaced than the database itself.

  2. What exactly is this separation buying you? And, just as important, what are you losing? Separation of Concerns (SoC) is a nice term that is thrown about quite a bit. However, most people rarely understand why they are Concerned with the Separation to begin with.

I bring these up because more often than not applications can benefit from a tighter coupling to the underlying data model. Never mind that most ORM's almost enforce a tight coupling due to the nature of code generation. I've seen lot's of supposedly SoC projects come to a crash during testing because someone added a field to a table and the DAL wasn't regenerated... This kind of defeats the purpose, IMHO...

Another factor is where should the business logic live? No doubt there are strong arguments in favor of putting large swaths of BL in the actual database itself. At the same time there are cases where the BL needs to live in or very near your domain classes. With BL spread in such a way, can you truly separate these two items anyway? Even those who hate the idea of putting BL in a database will fall back on using identity keys and letting the DB enforce referential integrity, which is also business logic..

Without knowing more, I would suggest you consider flattening the Data Access and Domain Model layers. You could move to a "provider" or "factory" type architecture in which the service layer itself doesn't care about the underlying access, but the factory handles it all. Just some radical food for thought.

NotMe
  • 87,343
  • 27
  • 171
  • 245
  • Interesting thoughts. In the old days when MTS came out, I'd just have my COM objets in MTS and use TS pattern to orchestrate the application behaviour using what I'd call a Data Layer but was probably more akin to a combined simple Domain / Data layer when I compare it to current patterns – Mark 909 Nov 30 '10 at 16:08
1

Until now I have seen that application can be well layered into three layers: Presentation-->Logic-->Data--and Entities (or Bussines Object). In the Logic Layer case you can use some pattern such as Transaction Script or Domain Model I'm supposing you're using this last. The domain model can use a Data Mapper for interacting with the data layer and create business objects, but you can also use a Table Module pattern.

All this patterns are described in Marttin's Fowler Patterns of Enterprise Application Architecture book. Personally I use Transaction Script because it is simplest than Domanin Model.

ArBR
  • 4,032
  • 2
  • 23
  • 29
1

One solution is to make your Data Access layer subclass your domain entities (using Castle DynamicProxy, for example) and inject itself into the derived instances that it returns.

That way, your domain entity classes remain persistence-ignorant while the instances your applications use can still hit databases to lazy-load secondary data.

Having said that, this approach typically requires you to make a few concessions to your ORM's architecture, like marking certain methods virtual, adding otherwise unnecessary default constructors, etc..

Moreover, it's often unnecessary - especially for line-of-business applications that don't have onerous performance requirements, you can consider eagerly loading all the relevant data: just bring the inventory items up with the order.

Community
  • 1
  • 1
Jeff Sternal
  • 47,787
  • 8
  • 93
  • 120
1

I felt this was different enough from my previous answer, so here's a new one.

Another approach is to leverage the concept of Inversion of Control (IoC). Build an Interface that your Data Access layer implements. Each of the DAL methods should take a list of parameters and return a Data Table.

The service layer would instantiate the DAL through the interface and pass that reference to your Domain Model. The domain model would then make it's own calls into the DAL, using the interface methods, and decide when it needs to load child objects or whatever.

Something like:

interface IDBModel {
  DataTable LoadUser(Int32 userId);
}

class MyDbModel : IDBModel {
  DataTable LoadUser(Int32 userId) {
    // make the appropriate DB calls here, return a data table
  }
}

class User {
  public User(IDBModel dbModel, Int32 userId) {
    DataTable data = dbModel.LoadUser(userId);
    // assign properties.. load any additional data as necessary
  }
// You can do cool things like call User.Save() 
// and have the object validate and save itself to the passed in 
// datamodel.  Makes for simpler coding.
}

class MyServiceLayer {
  public User GetUser(Int32 userId) {
    IDBModel model = new MyDbModel();

    return new User(model, userId);
  }
}

With this mechanism, you can actually swap out your db models on demand. For example, if you decide to support multiple databases then you can have code that is specific to a particular database vendors way of doing things and just have the service layer pick which one to use.

The domain objects themselves are responsible for loading their own data and you can keep any necessary business logic within the domain model. Another point is that the Domain Model doesn't have a direct dependency on the data layer, which preserves your mocking ability for independent testing of business logic.

Further, the DAL has no knowledge of the domain objects, so you can swap those out as necessary or even just test the DAL independently.

NotMe
  • 87,343
  • 27
  • 171
  • 245