views:

270

answers:

4

Hello everyone, I have a question about DDD. I'm building a application to learn DDD and I have a question about layering. I have an application that works like this:

UI layer calls => Application Layer -> Domain Layer -> Database

Here is a small example of how the code looks:

//****************UI LAYER************************
//Uses Ioc to get the service from the factory.
//This factory would be in the MyApp.Infrastructure.dll
IImplementationFactory factory = new ImplementationFactory();

//Interface and implementation for Shopping Cart service would be in MyApp.ApplicationLayer.dll
    IShoppingCartService service = factory.GetImplementationFactory<IShoppingCartService>();

    //This is the UI layer,
    //Calling into Application Layer
    //to get the shopping cart for a user.

    //Interface for IShoppingCart would be in MyApp.ApplicationLayer.dll
    //and implementation for IShoppingCart would be in MyApp.Model.
    IShoppingCart shoppingCart = service.GetShoppingCartByUserName(userName);

    //Show shopping cart information.
    //For example, items bought, price, taxes..etc
    ...

    //Pressed Purchase button, so even for when
    //button is pressed.
    //Uses Ioc to get the service from the factory again.
    IImplementationFactory factory = new ImplementationFactory();
    IShoppingCartService service = factory.GetImplementationFactory<IShoppingCartService>();
    service.Purchase(shoppingCart);


    //**********************Application Layer**********************
    public class ShoppingCartService : IShoppingCartService
    {
       public IShoppingCart GetShoppingCartByUserName(string userName)
       {
           //Uses Ioc to get the service from the factory.
           //This factory would be in the MyApp.Infrastructure.dll
           IImplementationFactory factory = new ImplementationFactory();

           //Interface for repository would be in MyApp.Infrastructure.dll
           //but implementation would by in MyApp.Model.dll
           IShoppingCartRepository repository = factory.GetImplementationFactory<IShoppingCartRepository>();

           IShoppingCart shoppingCart = repository.GetShoppingCartByUserName(username);
           //Do shopping cart logic like calculating taxes and stuff
           //I would put these in services but not sure?
           ...

           return shoppingCart;
       }

       public void Purchase(IShoppingCart shoppingCart)
       {
            //Do Purchase logic and calling out to repository
            ...
       }
    }

I've seem to put most of my business rules in services rather than the models and I'm not sure if this is correct? Also, i'm not completely sure if I have the laying correct? Do I have the right pieces in the correct place? Also should my models leave my domain model? In general I'm I doing this correct according DDD?

Thanks!

+2  A: 

DDD works best when services (application layer) expose only commands on domain objects, not the objects themselves.

In you example I would create a service method like Purchase(strng productName, int quantity) which internally would get a ShoppingCart from Repository (don't have to use interface here) get a Product (by its name) from Repository and call cart.AddProduct(product, quantity)

not, the business logic of adding products to cart, calculating total amount, total weight or other business stuff is encapsulated in the mode.

Szymon Pobiega
Thanks for your response. So the services should be static than and live in the Application Layer? I figured the services should implement an interface because it makes it easier to test. Also who is responsible for calling the business logic like calculating total amount? Does the repository do that?
Michael
If you call AddProduct on a cart, this cart is responsible for maintaining its own consistency so it should update any information that needs to be update (like total amount).Yes, it is good to implement interface in services, but since DDD says you should not place business logic in services, they are poor candidates for testing. But interfaces are useful when doing aspect oriented programming. I used to intercept invocation on my services and, in an AOP advice, begin/commit the transaction on database, log any exception and so on.
Szymon Pobiega
+2  A: 

When you ask

Should my models leave my domain model?

I would say that if at all possible you should work to avoid shuffling data back and forth between different sets of objects in one program. Doing that would result in you going through a lot of tedium writing a lot of boring and error-prone code, you will have a redundant set of DTO objects that have to mirror your domain objects, also you get less mileage out of the methods you write because they only operate on either the DTOs or domain objects. All in all it is very painful and doesn't accomplish much. I try to have one set of domain objects that is passed around throughout the application.

(That doesn't mean there aren't cases where you really have a need to move data between different sets of objects, like when building an anti-corruption layer. Just don't go looking for trouble if it's not absolutely necessary.)

As for

IShoppingCart shoppingCart = repository.GetShoppingCartByUserName(username);
//Do shopping cart logic like calculating taxes and stuff
//I would put these in services but not sure?

one of the goals of Domain-Driven Design is to try to separate business logic from infrastructure code. Whatever you can figure out how to calculate using only the domain objects is better off going into the domain model, where you can write tests for it that do not involve the database. I would want to have the service pull an aggregate of domain objects, then have the business logic calculations happen in the domain objects, then have the service save the state changes that the calculations brought about in the domain objects.

Nathan Hughes
A: 

You should have one thin layer between domain objects and services to manipulate with domain objects (recommended by Eric Evans). This object name is usually ending on “Manager”. For example PurchaseManager or similar. All logic that connect different domain objects (user, purchase, address …) is in this object.

  • Service call Manager.GetSomething()
  • Manager get data from different repositories, do business logic and return result
  • Manger Return Result but never return domain objects itself ! If you have domain object called User you should stick with naming convention you invent and map User to UserDataContract or similar. Don’t expose domain objects. It is extra work but you should not couple your domain model with UI.
  • Many times domain object has methods to do some business logic
Vladimir Kojic
Could you be specific about where the Eric Evans recommendation you reference came from? I don't remember anything like this in his book (I may have missed something, obviously).
Nathan Hughes
I found this as a reference in one of my documents (don’t have book with me right now):There are important domain operations that can’t find a natural home in an Entity or Value object. Since our modeling paradigm is objects, we should try to fit them into objects. Sometimes we force those operations into an appropriate object, gradually slipping towards procedural programming. We end up with objects heaving names ending with “Manager”. They have no state of their own nor any meaning in the domain beyond the operation they host.
Vladimir Kojic
@Vladimir - if I'm not mistaken, Evans is describing an *anti-pattern* in that section, not recommending it. He acknowledges that there are *occasionally* operations that don't fit into domain objects in which case it's appropriate to put those operations in a service - but strongly recommends that we try to avoid that and strive to put the logic into our domain model.
Jeff Sternal
@Jeff - I agree that word “recommend” is probably misleading (thanks for comment). But for majority of serious applications you don’t return just collections of objects. If you have object A, object B and object C and some service requires mix of them plus some serious logic and calculations I use “Manager” object to open repository of A,B,C and get result. Way cleaner than trying to push everything to A,B or C. And "Manager" is a part of Domain Model.
Vladimir Kojic
A: 

Thanks for everyones reply. However I think I'm even more confused than I was. Ok, I understand the point where a Application Layer should be a thin layer. What type of object should the Application Layer have to transition from UI Layer to Domain Layer? Should it be a Application Service, factory or something else and should it be an instance or static?

Why shouldn't a service have business logic? I thought that was the point of a Domain Service? For example a TaxService?

Does the repository call the business logic on the model? For example check to make sure username didn't go over max allowed credit for the account?

By what I've read my application should look more like this(btw, I vision better when I see code.)?

//**********************UI Layer**********************
//Once again IShoppingCart and ShoppingCartFactory would be in the Application Layer.
//This is the UI calling into the Application Layer
IShoppingCart shoppingCart = ShoppingCartFactory.GetShoppingCartByUserName(userName);



//**********************Application Layer**********************
static public class ShoppingCartFactory
{
   IShoppingCart GetShoppingCartByUserName(string userName)
   {
      IShoppingCartRepository repository = ImplementationFactory.GetImplementationFactory<IShoppingCartRepository>();
      return repository.GetShoppingCartByUserName(userName);
   }
}


//**********************Infrastructure Layer**********************
public class ShoppingCartRepository : IShoppingCartRepository 
{
    public IShoppingCart GetShoppingCartByUserName(string userName)
    {
          //Do DAL stuff to get all shopping items
          IShoppingCart shoppingCart = GetDataFromDAL(userName)

          IUserRepository repository = ImplementationFactory.GetImplementationFactory<IUserRepository >();
          double creditLimit = repository.GetCreditLimit(userName);

          //Maybe this service be a static rather
          //and instance?  Also someone said 
          //services shouldn't have business logic so I'm
          //assuming this might be wrong?
          ITaxService service = ImplementationFactory.GetImplementationFactory<ITaxService>();
          double taxes = service.GetTaxes(shoppingCart);
          shoppingCart.Total = shoppingCart.GetTotalForAllItems() + taxes;

          //I know this is a kinda a silly example.
          if(shoppingCart.Total > creditLimit)
          {
              //Do some logic to disable some Items
              //or put some type of error telling 
              //the user to remove some items.
          }
    return shoppingCart ;
    }
}


public class UserRepository : IUserRepository 
{
    public double GetCreditLimit(string userName)
    {
          //Do DAL stuff to get credit limit.
          ...

          return creditLimit;
    }
}

//**********************Domain Layer**********************
public class TaxService : ITaxService
{
      public double GetTaxes(shoppingCart)
      {
         //Do logic to return how much taxes
         //based on amount and state.
         ...

         return taxes;
      }
}

Anyway I"m still not sure if I'm grasping everything? Once again thanks for everyones help.

Michael
A few things that concern me here. 1) GetShoppingCartByUserName() sets a property of the cart before returning and also does credit limit validation. Based on it's name it should ONLY get the cart. Don't mix concerns. 2) The Total property is not set by the cart itself. Why not have shoppingCart.GetTotal(ITaxService taxService) and pass in the tax service instance? 3) Is GetCreditLimit() a repository concern or a user concern? Why not have a CreditLimit property on the user object? I think you should identify what your aggregates are and that will guide where these properties/methods belong.
Ryan
Yes, you're correct GetShoppingCartByUserName is doing to much. I'm still trying to improve on recognizing when a method or object is doing to much. With that said, all 3 points were done to show business logic in the repository and I was more less asking was the repository the right place for business logic? Where should the validation logic go? When you do shoppingCart.GetTotal(ITaxService taxService), will that method update the Total property or return the Total? How does Total get updated if GetShoppingCartByUserName() is not doing it?
Michael