views:

139

answers:

4

So, I'm developing some software, and trying to keep myself using TDD and other best practices.

I'm trying to write tests to define the classes and repository.

Let's say I have the classes, Customer, Order, OrderLine.

Now, do I create the Order class as something like

abstract class Entity {
    int ID { get; set; }
}

class Order : Entity {
    Customer Customer { get; set; }
    List<OrderLine> OrderLines { get; set; }
}

Which will serialize nice, but, if I don't care about the OrderLines, or Customer details is not as lightweight as one would like. Or do I just store IDs to items and add a function for getting them?

class Order : Entity {
    int CustomerID { get; set; }
    List<OrderLine> GetOrderLines() {};
}

class OrderLine : Entity {
    int OrderID { get; set; }
}

And how would you structure the repository for something like this?

Do I use an abstract CRUD repository with methods GetByID(int), Save(entity), Delete(entity) that each items repository inherits from, and adds it's own specific methods too, something like this?

public abstract class RepositoryBase<T, TID> : IRepository<T, TID> where T : AEntity<TID>
{
    private static List<T> Entities { get; set; }

    public RepositoryBase()
    {
        Entities = new List<T>();
    }

    public T GetByID(TID id)
    {
        return Entities.Where(x => x.Id.Equals(id)).SingleOrDefault();
    }

    public T Save(T entity)
    {
        Entities.RemoveAll(x => x.Id.Equals(entity.Id));
        Entities.Add(entity);
        return entity;
    }

    public T Delete(T entity)
    {
        Entities.RemoveAll(x => x.Id.Equals(entity.Id));
        return entity;
    }
}

What's the 'best practice' here?

+2  A: 

Entities

Let's start with the Order entity. An order is an autonomous object, which isn't dependent on a 'parent' object. In domain-driven design this is called an aggregate root; it is the root of the entire order aggregate. The order aggregate consists of the root and several child entities, which are the OrderLine entities in this case.

The aggregate root is responsible for managing the entire aggregate, including the lifetime of the child entities. Other components are not allowed to access the child entities; all changes to the aggregate must go through the root. Also, if the root ceases to exist, so do the children, i.e. order lines cannot exist without a parent order.

The Customer is also an aggregate root. It isn't part of an order, it's only related to an order. If an order ceases to exist, the customer doesn't. And the other way around, if a customer ceases to exist, you'll want to keep the orders for bookkeeping purposes. Because Customer is only related, you'll want to have just the CustomerId in the order.

class Order
{
  int OrderId { get; }

  int CustomerId { get; set; }

  IEnumerable<OrderLine> OrderLines { get; private set; }
}

Repositories

The OrderRepository is responsible for loading the entire Order aggregate, or parts of it, depending on the requirements. It is not responsible for loading the customer. If you need the customer, load it from the CustomerRepository, using the CustomerId from the order.

class OrderRepository
{
  Order GetById(int orderId)
  {
    // implementation details
  }

  Order GetById(int orderId, OrderLoadOptions loadOptions)
  {
    // implementation details
  }
}

enum OrderLoadOptions
{
  All,
  ExcludeOrderLines,
  // other options
}

If you ever need to load the order lines afterwards, you should use the tell, don't ask principle. Tell the order to load its order lines, and which repository to use. The order will then tell the repository the information it needs to know.

class Order
{
  int OrderId { get; }

  int CustomerId { get; set; }

  IEnumerable<OrderLine> OrderLines { get; private set; }

  void LoadOrderLines(IOrderRepository orderRepository)
  {
    // simplified implementation
    this.OrderLines = orderRepository.GetOrderLines(this.OrderId);
  }
}

Note that the code uses an IOrderRepository to retrieve the order lines, rather than a separate repository for order lines. Domain-driven design states that there should be a repository for each aggregate root. Methods for retrieving child entities belong in the repository of the root and should only be accessed by the root.

Abstract/base repositories

I have written abstract repositories with CRUD operations myself, but I found that it didn't add any value. Abstraction is useful when you want to pass instances of subclasses around in your code. But what kind of code will accept any BaseRepository implementation as a parameter?

Also, the CRUD operations can differ per entity, making a base implementation useless. Do you really want to delete an order, or just set its status to deleted? If you delete a customer, what will happen to the related orders?

My advice is to keep things simple. Stay away from abstraction and generic base classes. Sure, all repositories share some kind of functionality and generics look cool. But do you actually need it?

Niels van der Rest
A: 

Why not create separate Order classes? It sounds to me like you're describing a base Order object, which would contain the basic order and customer information (or maybe not even the customer information), and a separate Order object that has line items in it.

In the past, I've done as Niels suggested, and either used boolean flags or enums to describe optionally loading child objects, lists, etc. In Clean Code, Uncle Bob says that these variables and function parameters are excuses that programmers use to not refactor a class or function into smaller, easier to digest pieces.

As for your class design, I'd say that it depends. I assume that an Order could exist without any OrderLines, but could not exist without a Customer (or at least a way to reference the customer, like Niels suggested). If this is the case, why not create a base Order class and a second FullOrder class. Only FullOrder would contain the list of OrderLines. Following that thought, I'd create separate repositories to handle CRUD operations for Order and FullOrder.

jwiscarson
What will happen if there are two other properties in `Order` that you may or may not want to load, such as an invoice address and a shipping address? This will result in 2 ^ 3 = 8 possible loading scenarios. Will you create a separate class and repository for each combination? See [this question on such function parameters](http://stackoverflow.com/q/3324394/332100) for why this isn't a bad practice per definition.
Niels van der Rest
I didn't suggest, nor would I ever, that you apply this in all situations. I said that it depended on his overall class design. In this specific scenario of order lines and orders, I think it makes plenty of sense. I have to add, however, that I disagree with your conclusion regarding function naming schemes in the answer you linked. Aptly named functions are self-descriptive, but functions with parameters that describe some intrinsic behavior require a greater investment of time by any random programmer supporting the project after completion.
jwiscarson
A: 

I would divide my project up into the relevant parts. Data Transfer Objects (DTO), Data Access Objects (DAO). The DTO's I would want to be as simple as possible, terms like POJO (Plain Old Java Object) and POCO (Plain Old C Object) are used here, simply put they are container objects with very little if any functionality built into them.

The DTO's are basically the building blocks to the whole application, and will marry up the layers. For every object that is modeled in the system, there should be at least one DTO. How you then put these into collections is entirely up to the design of the application. Obviously there are natural One to many relationships floating around, such as Customer has many Orders. But the fundamentals of these objects are what they are. For example, an order has a relationship with a customer, but can also be stand alone and so needs to be separate from the customer object. All Many to Many Relationships should be resolved down into One to Many relationships which is easy when dealing with nested classes.

Presumably there should be CRUD objects that appear within the Data Access Objects category. This is where it gets tricky as you have to manage all the relationships that have been discovered in design and the lifetime models of each. When fetching DTO's back from the DAO the loading options are essential as this can mean the difference between your system running like a dog from over eager loading, or high network traffic from fetching data back and fourth from your application and the store by lazy loading.

I won't go into flags and loading options as others here have done all that.

    class OrderDAO
    {
    public OrderDTO Create(IOrderDTO order)
    {
    //Code here that will create the actual order and store it, updating the 
flelds in the OrderDTO where necessary. One being the GUID field of the new ID. 
I stress guid as this means for better scalability.

    return OrderDTO
    }

}

As you can see the OrderDTO is passed into the Create Method.

For the Create Method, when dealing with brand new nested Objects, there will have to be some code dealing with the marrying up of data that has been stored, for example a customer with old orders, and a new order. The system will have to deal with the fact that some of the operations are update statements, whilst others are Create.

However one piece of the puzzle that is always missed is that of multi-user environments where DTO's (plain Objects) are disconnected from the application and returned back to the DAO for CRUD. This usually involves some Concurrency Control which can be nasty and can get complicated. A simple mechanism such as DateTime or Version number works here, although when doing crud on a nested object, you must develop the rules on what gets updated and in what order, also if an update fails concurrency, you have to decide on whether you fail all the operation or partial.

WeNeedAnswers
By the way, I would avoid using inheritance when dealing with Entities and the repository. I would try to do some injection (Inversion of Control) instead and object composition using interfaces.
WeNeedAnswers
DTO's are usually 'dumb' objects; they don't contain business logic. Where would you put the business logic? I have to agree that concurrency control *is* hard. If this is a crucial aspect of the application, a [CQRS architecture](http://www.udidahan.com/2009/12/09/clarified-cqrs/) is probably more appropriate.
Niels van der Rest
Depends what is meant by business logic, it seems to change. If you mean business rules, would have to be in the DAO Layer/Repository. This is where I get a bit fuzzy. The database seems the best place to put business rules, update logic etc, but if your going domains then either you keep the domain and the database one and the thing, or you put in another layer that deals with all the rules. For example, is the customer credit worthy, do they qualify for a discount if they order x,y,z etc. The business rules are separate from the DTO objects, but part of the Domain Layer.
WeNeedAnswers
The Domain layer is not just a single object, but a collections of objects working in unison. The Domain layer also gets split down into sub-layers. As long as the model remains intact and is in one place I see no problem with this approach, others would say that a Domain object should be self contained. But how do you deal with the dependency problems of related objects and the rules to apply to them. If you go the Business Rules Layer you can solve this by adding a new dependency rule that propagate to other rules where required.
WeNeedAnswers
A: 

If you are interested in domain driven design (DDD) implementation with POCOs along with explanations take a look at the following 2 posts:

http://devtalk.dk/2009/06/09/Entity+Framework+40+Beta+1+POCO+ObjectSet+Repository+And+UnitOfWork.aspx

http://www.primaryobjects.com/CMS/Article122.aspx

There is also a project that implements domain driven patterns (repository, unit of work, etc, etc) for various persistence frameworks (NHibernate, Entity Frameworks, etc, etc) called NCommon

zam6ak