Repositories don’t have save methods

Here’s a repository from an application I’ve been working on recently. It has a pretty significant leaky abstraction problem that I shall be fixing tomorrow:

public interface IEmployeeRepository
    void Add(Employee employee);
    void Remove(Employee employee);
    void GetById(int id);
    void Save(Employee employee);

What’s wrong with this picture?

Let me quote the DDD step by step wiki on what exactly a repository is:

Repositories behave like a collection of an Aggregate Root, and act as a facade between your Domain and your Persistence mechanism.

The Add and Remove methods are cool — they provide the collection semantics. GetById is cool too — it enables the lookup of an entity by a special handle that external parties can use to refer to it.

Save on the other hand signals that an object’s state has changed (dirty), and these changes need to be persisted.

What? Dirty tracking? That’s a persistence concern, nothing to do with the domain. Dirty tracking is the exclusive responsibility of a Unit of Work — an application-level concept that most good ORMs provide for free. Don’t let it leak into your domain model!

October 22, 2009


Rohland on October 23, 2009 at 8:57 pm.

Hi Richard,

Just interested, how would you implement the update mechanism in this case?


Mike on October 24, 2009 at 2:20 am.

I agree mostly but sometimes “Save” is part of the ubiquitous language everyone agrees to.

Mathias on October 24, 2009 at 9:26 am.

+1 to Rohland’s question! Is your Add an “Add or Replace”, and if not, how do you handle updates?

Richard on October 24, 2009 at 9:44 am.

Rohland and Mathias: Add is just add, you only call it the first time an entity is made persistent. Updates to existing objects are handled via the unit of work; it keeps track of all loaded entities and at the end it looks for any that changed and saves them.

Both NHibernate’s ISession and Linq-to-SQL’s DataContext implement this pattern.

Richard on October 24, 2009 at 9:48 am.

Mike: save is a frequently used term but so is login. Unless your domain is a document editor or security gateway, both are application concepts, not part of the domain model.

Matt Freeman on October 24, 2009 at 6:38 pm.

I think I agree, although I have no yet adopted this approach myself, it should be a simple refactor. Infact my save method is pretty dumb since when the UOW is commited any dirty entities are flushed via nhibernate.

Rohland on October 26, 2009 at 7:33 pm.

Richard, in many cases you need to explicitly force an update as part of a larger unit of work. I am not sure if I prefer the SubmitChanges approach as implemented with LINQ to SQL.

For example, I don’t enjoy having to pass the data context to each repository. Obviously, you could implement some kind of IOC container so you don’t deal with the binding explicitly but when I have tried this in the past I have run into some weird runtime issues. Case in point – the LoadWith option in LINQ to SQL just doesn’t really work if you implement this kind of pattern because the options need to be set before the context is first utilised. This causes a headache in terms of lifetime management of the data context.

I have been using LLBLGEN for the past 6 months or so and have really been impressed with the library. Granted, my repositories have a “Save” method, but I am not sure if this is such a big issue.

Leave a Reply