DDD (Aggregate Root) / MVC

205 views
Skip to first unread message

W3Max

unread,
Aug 11, 2009, 9:16:10 PM8/11/09
to S#arp Architecture
I know that we should only create repositories for Aggregate Roots and
not for the entities it aggregates. Should I apply the same logic to
MVC, e.g., have a Controller/PageView for an Aggregate Root and then a/
some PartialView/PartialAction for the sub-entities.

For example, I would like to create an Order that aggregates
OrderLines. Who is responsible for creating the objects Order and
OrderLine in MVC, the controller or ModelBinder (I mostly work with
strongly typed views/controllers)? Do I have to create a special
ModelBinder that will create an Order and the OrderLines it aggregates
in one step?

If the process is in multiple steps (UI) (create an order, add 1 or
more OrderLines), does it still make sense to design Order as an
aggregate root?

Am I mixing some concepts? I know that views can work with DTOs?

I asked a similar question here :
http://stackoverflow.com/questions/1242381/asp-net-mvc-ddd-create-and-update-actions-for-aggregate-roots

Thank you !

W3Max

unread,
Aug 13, 2009, 8:51:51 PM8/13/09
to S#arp Architecture
Is my question dumb or nobody as an answer (I doubt it)?

Maybe it is because I should not talk about business model when
referring to the UI? But at some point, I still have to load an entire
object (aggregate root) from the database to feed my views...

It would be great if more complexes examples would be demonstrated in
the Northwind sample project... not to diminish the project (sharp
architecture) which is already excellent!

Thanks

joedirt

unread,
Aug 14, 2009, 8:50:15 AM8/14/09
to S#arp Architecture
I'll take a turn at answering.

I think you may have some misconceptions (or more likely I am
misreading) about what should happen. Using your Order/OrderLines
example and starting from the Core, here is how I would do it.

There should be an OrderLine object and an Order object which has a
list of OrderLine objects inside it. This makes the Order the root
aggregate. In the OrdersController, you would have the
IRepository<Order> member which would give the Controller access to
pull and save Order objects from the database (via Get, GetAll, Find,
FindOne, SaveorUpdate, etc) The View could use the Order aggregate as
is in a strongly type view to display the Order and cycle through the
OrderLine list to display the individual OrderLine objects. Typically
the "Business Logic" is inside the Order object to check various
conditions before getting saved back to the database.

This is the standard pattern and I'm sure you're familiar with it; yet
it doesn't work well when the pattern hits the complexity of reality.

I have found that I need to use an ApplicationService which contains
multiple repositories because the view demands that not only can a
Order root aggregate be used, but also a list of Customers, and a list
of ServiceLocations, and a list of.... whatever. I used the
ApplicationService to make it so the controller only needs one member
to use and get all the different things out of the database. This
also allows for me to put business logic for things that have
interdependencies (Orders/Customters/etc.) into one location.

I have also found that the root aggregate objects don't always work
well as the model for a strongly typed view. This isn't new, and is
actually in the Northwind example (search for ViewModel). These
ViewModels are DTOs which are used in the conversation between the
controllers and views. Being a DTO, they are not actually real core
objects and don't have a mapping to the database, but are custom
(usually thinner) objects that are made up of a collection of
Properties. These DTOs don't have business logic, don't have
validation, and usually exist as classes inside the controller class.
When a controller method is called, the controller calls the
Repository or ApplicationService to get a Core object (an Order, for
example), transform it into a ViewModel, and send that to the strongly
typed view. The View displays that ViewModel, manipulates it, and
sends it back to the controller via ModelBinding. The controller then
transforms it back to the appropriate Core object (or objects) and
calls the Repository or ApplicationService to save the objects back to
the database.

Now, this seems like duplication of objects (Core and ViewModel), and
in _some_ cases it is. However, in many cases, the Core objects work
just as is for the round trip from the Core to Controller to View and
back again. This greatly depends on your design. Given a good
design, the database should easily convert into Core objects which can
be used most of the time as is in the Views. But again, when reality
crashes in, you'll find that ViewModel objects and the transformation
to/from Core objects isn't too bad a deal.

Does this help?

Joe

Howard Pinsley

unread,
Aug 14, 2009, 9:05:34 AM8/14/09
to S#arp Architecture
Joe,

Nice post. I have found that I need to introduce a ViewModel whenever
the view has a dropdown list. For example, my view may require a
dropdown list of hospitals:

Html.DropDownList("HospitalId", Model.Hospitals)

My ViewModel will contain:

public IEnumerable<SelectListItem> Hospitals { get; set; }

This would be a list of all hospitals -- something I would not find in
my core model object.

I have used AutoMap to map between model and viewmodel objects. If
someone thinks I've made things more complex than they need to be, I'd
like to hear about it. :-)

JB

unread,
Aug 14, 2009, 4:28:03 PM8/14/09
to S#arp Architecture
Joedirt and I work together and have spent many hours discussing this
sort of thing. And as he stated "reality" tends to make the lines blur
in the implementation of all of this stuff. Let me give an example of
how complicated something as simple as adding an OrderLine to an Order
can be. To start let's just define a couple interfaces for Order,
OrderLine, and Item:

public interface Order
{
public string Number { get; }
public IList<OrderLine> Lines { get; }
}

public interface OrderLine
{
public int LineNumber { get; }
public double Quantity { get; set; }
public Item Item { get; set; }
}

public interface Item
{
public string Number { get; }
public double Price { get; }
}

On the surface nothing here seems that overly complicated. In the
controller whenever I want to add a new OrderLine I simply go the
Order repository and get the Order. Then I instantiate a new OrderLine
object. But wait, I'm also going to have to go to the Item repository
and get the Item. After I have my newly instantiated OrderLine, I
simply add the new OrderLine to the Order's Lines collection and save
the Order. If things stay this simple you could create your
controller's constructor with the Order and Item repositories as
arguments. We've found that in "reality" nothing is ever this simple
and there will probably be a couple extra repositories we'll need to
call on to populate drop down list, etc. So our common practice is to
create an application service that utilizes the various repositories.

This all seems fairly simple and uncomplicated. But what happens when
you are working with Orders that have thousands of OrderLines? Sure
you can make the Lines collection lazy in your mappings. But that
still isn't very efficient. To display the Order and OrderLines in
your view once you iterate over the Lines collection the lazy
association will kick off the SQL and return the OrderLines which is
what lazy associations are for.

But what about when you go to save a new OrderLine? When you hit that
action in your controller you get the Order and because the Lines
collection is lazy you don't fire off the SQL for that. But as soon as
you call Lines.Add(newOrderLine), BOOM, you've kicked off your SQL.
This is surely going to start to slow things down.

So what do you do? Well, like most of us you abandon the aggregate
root and start saving OrderLines on their own. Which sort of breaks
all the rules about working with domain aggregates, doesn't it? Then
the question that invariably arises is do I now need a controller for
working with OrderLines? And what if my final Orders html page uses
AJAX callbacks to add new OrderLines? Should that page be calling
different controllers depending on weather it is updating the Order or
adding new OrderLines?

These are the types of things we face every day in the real world.
Even something as simple as Order/OrderLines can be very complicated
to implement. I'm not sure what the answer is. And I'm not sure all of
the experts on DDD even know what all the answers are. I do know that
these forums are a great place to discuss all of this stuff and that
by taking the time to share on them we all benefit in the end.

W3Max

unread,
Aug 14, 2009, 7:10:58 PM8/14/09
to S#arp Architecture
Thanks to Joe and JB for their experiences.

I'm currently thinking the same : "[...] I'm not sure all of
the experts on DDD even know what all the answers are." (JB)

But to get back to my initial question, let me start be clearing
things up a little...

What I would like to know is if I should (in a MVC project) always use
a ModelBinder (SharpModelbinder) or, in cases like the Order/OrderLine
- when there is more than one object to create (1 aggregate root with
all the relation objects it aggregates) - would it be better to do the
job inside the controller action method (Update/Create). From what I
have read, I should keep the action methods of a controller "thin".
But is there a way to let the SharpModelBinder (or a class that
inherits from it) create an aggregate root?

I already use an ApplicationService to populate the DTO that is passed
to the view. (as in the Northwind sample)
But the Create/Update action method of my controller should generate
(ModelBinder) a business object, not a DTO.
I am also trying to separate the views

OrderForm.ascx uses OrderLineForm.ascx (ViewUserControls)
Create.aspx/Edit.aspx (Views)

I would like to read Billy McCafferty's opinion... an example would
even be better... but I'm just dreaming.

With regards,

W3Max

P.S.: Maybe my english is not clear - sorry for that - I am not
english so it's sometimes hard for me to explain the problem clearly.
Reply all
Reply to author
Forward
0 new messages