Search code examples
repositorydomain-driven-designfactoryaggregateroot

DDD and MVC : The Contoller gets the AggregateRoot from Factory not Repository? huh?


I recently started on a project with an existing database (Oracle) and MVC 4. Lot's of coding has already occurred.. but there's no "strategy" in the code.. just DB -> ORM -> Controller. So i'm trying to add some flare to the development as well as practice some DDD development techniques.

I have defined a couple Aggregate Roots, Each has a repository which deals with saving them and deleting (and their children), etc.. one of these Aggregate Roots has reference to another Aggregate Root, and it deals with it's "child objects" through it.

example:

A Client has one or more purchase orders which has Line Items, 
if a client wants to add a line item to the purchase order, 
it has to go through the purchase order.

that's fine.. client Aggregate Root, purchase order Aggregate Root.

Now, some Services have also popped out, like a service which modifies the statuses of the purchase order, it removes the burden from the Purchase Order AR, and it's nice and clean and purposed (it could be used by other "things" to update the status of a purchase order), (maybe that should be a part of the Purchase Order AR? a minor detail..)

The repositories are currently doing their job of persisting the data from the database and "fills up" the AR's with data. when the AR "saves" it, the repository, saves anything that needs saving. The Purchase Order Repository is used by the Client AR so the client can load up any Purchase orders it may contain.. Hopefully I'm on the right track.

Now, enter MVC. So I've also got some ViewModels, which are basically display definitions on what needs to be pumped out to the User. Automapper has proved frickin AWESOME, so i can just "auto map" to the view model. No brains needed, perfect..

NOW, the implementation details which are really throwing me off..

The controller currently is working through a Client Factory, which returns a Client AR, which can then do anything that the purchase order list controller will need to do, which is manage associated purchase orders (as a whole, and not the purchase orders details or data in this case).

So now i want to make sure this is correct.. because a lot of the examples I see have the controllers working with repositories, not factories, but I have also seen factories being recommended for creating AR's.. which leads me to believe that in the examples, controllers are dealing with aggregate roots, but such a way that it requires the "consumer" has to query the AR in order to Get an AR:

Like:

Get the Client Aggregate where the ClientID is 15

Or better yet:

get the Client Aggregate, where the ClientID is 15 
and where active purchase orders > 0 ...or something..

Which could look like:

ClientAR = ClientRepository.GetClientByIDAndHasActivePurchaseOrders(15);
resultsImLookingFor = ClientAR.PurchaseOrders(); //or something

I feel like in this case, the repository would have "Filled" the ClientAR to my needs, so now i have this ClientAR thing which varies based on it's usage, it smells to me..

Using a Factory "Feels Better" because i'm just creating a ClientAR from the Factory and then using it, it's not changing based on situation.. it is what it is..

ClientAR  = ClientFactory.CreateClient(15) // returns a ClientAR 
resultsImLookingFor = ClientAR.GetPurchaseOrdersByStatus(statusID);

Or Maybe I"m totally missing it and I should be doing this::

ClientAR = ClientRepository.GetClientByID(15, PurchaseOrderSpec)

Am I missing the specification side of things? (At this point, i dont have enough to really get going with specs (just yet) because i need to get this things working)

I'm trying not to get bogged down into the details of implementation, because of course my bossman doesn't give a crap about how i do it.. thus far, implementing things with at least thinking about DDD (hopefully i'm getting it) has proven pretty nice with the testablility and the Logical "boundaries" of responsibility that results from this "pattern" or "way of thinking" i guess I should say..

So, am i approaching this correctly? if it's arguable, i'm okay with that.. if it's just plain WRONG then guidance is definitely appreciated, and if i'm way off, constructive criticism wont hurt my feelings if there's a good reason behind it..

thanks in advance.


Solution

  • So i'm trying to add some flare to the development as well as practice some DDD development techniques.

    Flare isn't usually a desirable quality for a project and can be counterproductive leading to acronym-driven development, resume-driven development, etc. The same goes for DDD - don't try to apply the DDD tactical patterns without understanding the drawbacks.

    one of these Aggregate Roots has reference to another Aggregate Root

    References between ARs should be restricted to identity references, not object references wherever possible. An AR should define a consistency boundary which may not necessarily lead to a model which mirrors reality in the most natural way. Expressing the relationship between a client and a PO can be accomplished with an identity reference - a PO has a client ID on it. Take a look at Effective Aggregate Design by Vaughn Vernon for more on this.

    maybe that should be a part of the Purchase Order AR?

    Any modifications of entity state should be encapsulated by the entity. Domain services can provide functionality to entities where that functionality does not naturally fit into an existing entity.

    The repositories are currently doing their job of persisting the data from the database and "fills up" the AR's with data. when the AR "saves" it, the repository, saves anything that needs saving. The Purchase Order Repository is used by the Client AR so the client can load up any Purchase orders it may contain.. Hopefully I'm on the right track.

    Neither an AR nor entities or value objects should reference a repository or call any methods on it. Repositories should be called by an application service. It is unlikely that a Client AR should contain a collection of POs. Instead, a list of a client's PO should be provided by the PO repository. The relationship is still there, it is just implemented with a repository instead of an object traversal. The reason for this goes back to the AR being a consistency boundary.

    The controller currently is working through a Client Factory

    A factory should only be used to create new instances, not access persisted instances - this is what a repository is for. You can architect the presentation layer (MVC) in a few ways. A typical DDD architecture is to have controllers reference application services. Application services in turn implement specific use cases by coordinating repositories, factories, infrastructure services and invoking behaviors on ARs. Say you have a use case where a client creates a new PO. The code would look something like:

    public ActionResult CreatePurchaseOrder(CreatePurchaseOrderViewModel viewModel)
    {
       var poData = viewModel.CreatePurchaseOrderData();
    
       this.purchaseOrderAppService.CreatePurchaseOrder(viewModel.ClientId, poData);
    
       return RedirectToAction("Index");
    }
    
    ...
    
    public class PurchaseOrderAppService
    {
      readonly IClientRepository clientDb;
      readonly IPurchaseOrderRepository poDb;
    
      public void CreatePurchaseOrder(int clientId, PurchaseOrderData poData)
      {
         var client = this.clientDb.Get(clientId);
    
         var purchaseOrder = PurchaseOrderFactor.Create(client, poData);
    
         this.poDb.Add(purchaseOrder);
         this.poDt.Commit(); // committing of Unit of Work should be moved up to infrastructure level
      }
    }
    

    Take a look a this DDDSample.Net project which contains an MVC UI implementation for a DDD project.