Mark 909
Mark 909

Reputation: 1835

Enterprise Design Pattern Question

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

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

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?

Upvotes: 2

Views: 2142

Answers (9)

ChrisLively
ChrisLively

Reputation: 88054

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.

Upvotes: 1

Jeff Sternal
Jeff Sternal

Reputation: 48583

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.

Upvotes: 1

ArBR
ArBR

Reputation: 4082

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.

Upvotes: 1

sebagomez
sebagomez

Reputation: 9599

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

Upvotes: 1

Oded
Oded

Reputation: 498992

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.

Upvotes: 4

ChrisLively
ChrisLively

Reputation: 88054

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.

Upvotes: 1

StriplingWarrior
StriplingWarrior

Reputation: 156504

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.

Upvotes: 1

Paul Sasik
Paul Sasik

Reputation: 81429

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.

Upvotes: 1

Shiraz Bhaiji
Shiraz Bhaiji

Reputation: 65391

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.

Upvotes: 2

Related Questions