Eric Anastas
Eric Anastas

Reputation: 22233

How do I determine the ID of an aggregate root added to a repository?

Say I have a generic repository interface as follows:

public interface IRepository<T>
{
    Add(T item);
    Delete(int itemId);
    Update(T item);
}

Typically the new ID of an item added through IRepository.Add() would be determined by some back-end database, but only once the overall transaction/unit of work has been submitted. So I'm fairly certain that it would be wrong for IRepository.Add() to return the new ID of the added item. The repository really shouldn't know anything about how ID are created. Is this correct?

If this is a case how else can one determine the new ID of an item added to a repository, or should I even be doing this? I know an ORM like NHibernate is able to automagically replace objects in memory with new objects with the correct ID, but I'm trying to design my repository with out any specific ORM implementation in mind.

For example say I have a website where customers can make orders. A new customer chooses to check out and is sent to a form to fill out their details. This information is used to create a Customer object which is stored in a CustomerRepository. Now their order information needs to be created but an Order needs to reference a Customer by their ID?

Customer newCustomer = new Customer(first, last, address, phone dateOfBirth);
customerRepository.Add(newCustomer);

//How would I determine customerId??
Order newOrder = new Order(customerId, shippingAddress, billingAddress);
newOrder.AddOrderItem("widget");
newOrder.AddOrderItem("doohicky");
newOrder.AddOrderItem("stuff");

Upvotes: 4

Views: 604

Answers (5)

Mohamed Abed
Mohamed Abed

Reputation: 5123

Domain entities should have their own ID strategy regardless database IDs, so preferably generate your id in the domain layer, or if you really need to generate id in database, then add another domain identifier generated at domain layer beside the database auto generated id.

In domain driven design where you apply repository pattern you should not tie your domain with database so relying on database for id creation is not a good idea.

Another point is that you may want to make customer associated in order not just putting the customer id, this makes the domain layer rich and solid.

Upvotes: 0

Steve Wilkes
Steve Wilkes

Reputation: 7135

In the example you give, I would create the Customer and Order in one step, and pass domain objects to domain objects, instead of passing Ids:

Customer newCustomer = new Customer(first, last, address, phone dateOfBirth);

// Pass the customer rather than the CustomerId:
Order newOrder = new Order(newCustomer , shippingAddress, billingAddress);
newOrder.AddOrderItem("widget");
newOrder.AddOrderItem("doohicky");
newOrder.AddOrderItem("stuff");

customerRepository.Add(newCustomer);
orderRepository.Add(newOrder);

// SaveChanges()

...when the changes are saved, the framework automatically populates the Ids of both Customer and Order, and fills in Customer.Id, Order.customerId, (etc.) by virtue of the Customer object having been assigned to the Order.

Upvotes: 3

Tomas Dermisek
Tomas Dermisek

Reputation: 778

Your interface is more DAO than Repository according to DDD:

http://codebetter.com/iancooper/2011/04/12/repository-saveupdate-is-a-smell/

Like Steve Wilkes mentioned, you should keep reference of Customer in Order and not Customer Id so when Unit of Work is processed, it will create correct link those Entities in Persistence Storage (SQL DB, Web Service etc)

For more on DAO here: http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html

Upvotes: 0

Tim Rogers
Tim Rogers

Reputation: 21723

Whether or not you use NHibernate, I feel that the approach it takes is the right one. Your goal with any domain objects is to only ever have one instance of that object in memory at any one time, i.e. you should never have two objects that represent the same database record. It follows that if the database has updated the record with a new id, the domain object in memory should also be updated with that ID since that is the "one" true representation of that record.

After calling Add, the object's ID is set and you could then make further changes to that object and call Update without having to know too much about your implementation.

Upvotes: 0

jim tollan
jim tollan

Reputation: 22485

Eric,

In the scenario you mention, I don't see any CommitChanges() going on. I would wrap everything in a transactionscope and then hit customerRepository.CommitChanges() before you add the orderlines. you should then be able to grab the id from the newly created customer object and use it as follows:

Order newOrder = new Order(newCustomer.Id, shippingAddress, billingAddress);

then, if the order(s) fails, you can roll everything back and keep it atomic by not hitting scope.Complete().

hope this helps..

Upvotes: 1

Related Questions