Question

The question title is not good, but I couldn't think of a way to word it better.

I am used to develop using POCO classes in my projects, that is, domain objects contain absolutely no logic, only data.

I am wondering which is the best approach when dealing with class relationships - to put a collection inside the class or to request it later.

For example, consider we have an e-commerce application, that has Orders, and Items inside those Orders. So we could have (pseudo-code)

class Order
{
     int id;
     List<OrderItem> Items;
}

class OrderItem
{
     Product product;
     int quantity;
}

And then use it (1)

var o = LoadOrder(id);
foreach (var i in o.Items) {

...or we could do:

class Order
{
    int id;
}

And use it this way: (2)

var odr = LoadOrder(id);
var itms = LoadItemsOfOrder(odr);
foreach (var i in itms) { 
    .....

The first way is more compact and more "OOP-ish", but the second way gives more control on how and when items are requested, e.g. what if now I need to request only the items that have quantity > 3 ? And what if I don't use the Items collection at all (e.g. to display only a list of orders) - I will be doing unnecessary queries to the database.

In my older, big projects I've gone with a hybrid approach - the domain classes are of the first flavor but there are various "load" methods, like "LoadOrder" and "LoadOrderWithItems" or "FillOrderObject".

Now I am working on a small, toy project, no customers and crazy deadlines and got time to think on these issues.

I tried searching the web but it is very hard to find a POCO answer not related to Entity Framework. (I am not working in .net this time, but would like to apply these concepts.)

Question: is there a method which is clearly better than the other, or both are valid and "it depends" on my project, if I am aiming for performance or for code clarity and maintainability?.

Était-ce utile?

La solution

It's never okay to satisfy hypothetical performance goblins at the expense of fewer lines of code or an API that more clearly expresses the relationship between entities.

So, the first example is preferred. Any decent ORM should be able to lazy-load the collection, and some of the more advanced ones will even let define a property on an entity with a custom loading function, also lazy-loaded. That takes care of both of your use cases without breaking encapsulation of the Order class and exposing its inner data structure.

Autres conseils

From the TDD point of view I would prefer pocos with collections over having specialalized LoadSubItems methods because it is much easier to create a testOrder with one orderitem than to mock a databaseLoadMethod to implement a test for a order processing service.

I am used to develop using POCO classes in my projects, that is, domain objects contain absolutely no logic, only data.

First off, your objects should not be only data. Behavior is central to you domain, and thus your domain objects should contain behavior logic. If anything they should contain all your behavior logic.

Domain objects that are just data would be what Martin Fowler calls an anemic domain model

POCO simply means you don't create your objects in your domain model with dependencies to external libs or frameworks. You don't make your classes all children of FunkyORMLibrary7 or something. The objects should be plain old objects that can be run without any external modules being loaded, this reduces dependency and also makes them easy to test in isolation. But that doesn't mean no logic in them, quite the opposite.

I am wondering which is the best approach when dealing with class relationships - to put a collection inside the class or to request it later.

You should be restricting who has responsibility to send messages to Order Item objects based on responsibilities. Who is responsible for ensuring the integrity of the who order including all the items in an order. In both your examples you are pulling order items out of the Order that contains them and operating on the items independently. That, in most cases, is a bad idea.

Think of a real world example. If you worked in a company would you let anyone in the company alter the information on an order document? I would imagine not, you would end up with a situation where the janitor just turned an order for 5 iPhones into an order for 50 million iPhones.

Some where you will need domain logic that controls what is or isn't valid actions to take on an order item. Only the object that contains this logic should be allowed operated on order items.

It makes far more sense that Order Items are accessed only through the Order object that contains them, since this is probably where all that logic will be held. In this case neither of your examples works. The for loop that is in both examples should be in the Order object, not outside of it and nothing should be able to iterate over the order items other than the order object itself.

Unless you have a very very good reason for accessing an individual order item outside of the order that contains it the rest of your system shouldn't be able to access the order item outside of the order that contains it. That is the key notion of the principle of encapsulation in OO and of an aggregation root in DDD.

Again from Martin Fowler DDD Aggregate

A DDD aggregate is a cluster of domain objects that can be treated as a single unit. An example may be an order and its line-items, these will be separate objects, but it's useful to treat the order (together with its line items) as a single aggregate ... Any references from outside the aggregate should only go to the aggregate root. The root can thus ensure the integrity of the aggregate as a whole.

Licencié sous: CC-BY-SA avec attribution
scroll top