views:

148

answers:

4

I've commonly seen examples like this on business objects:

public void Save()
{
     if(this.id > 0)
     {
          ThingyRepository.UpdateThingy(this);
     }
     else
     {
          int id = 0;
          ThingyRepository.AddThingy(this, out id);
          this.id = id;
     }
}

So why here, on the business object? This seems like contextual or data related more so than business logic.

For example, a consumer of this object might go through something like this...

...Get form values from a web app...

Thingy thingy = Thingy.CreateNew(Form["name"].Value, Form["gadget"].Value, Form["process"].Value);
thingy.Save();

Or, something like this for an update...

... Get form values from a web app...

Thingy thingy = Thingy.GetThingyByID(Int32.Parse(Form["id"].Value));
Thingy.Name = Form["name"].Value;
Thingy.Save();

So why is this? Why not contain actual business logic such as calculations, business specific rules, etc., and avoid retrieval/persistence?

Using this approach, the code might look like this:

... Get form values from a web app...

Thingy thingy = Thingy.CreateNew(Form["name"].Value, Form["gadget"].Value, Form["process"].Value);
ThingyRepository.AddThingy(ref thingy, out id);

Or, something like this for an update...

... get form values from a web app ...

Thingy thingy = ThingyRepository.GetThingyByID(Int32.Parse(Form["id"].Value));
thingy.Name = Form["Name"].Value;
ThingyRepository.UpdateThingy(ref thingy);

In both of these examples, the consumer, who knows best what is being done to the object, calls the repository and either requests an ADD or an UPDATE. The object remains DUMB in that context, but still provides it's core business logic as pertains to itself, not how it is retrieved or persisted.

In short, I am not seeing the benefit of consolidating the GET and SAVE methods within the business object itself.

Should I just stop complaining and conform, or am I missing something?

A: 

While I have no understanding of DDD, it makes sense to have 1 method (which will do UPSERT. Insert if record doesn't exist, Update otherwise).

User of the class can act dumb and call Save on an existing record and Update on a new record. Having one point of action is much clearer.

EDIT: The decision of whether to do an INSERT or UPDATE is better left to the repository. User can call Repository.Save(....), which can result in a new record (if record is not already in DB) or an update.

shahkalpesh
A: 

If you don't like their approach make your own. Personally Save() instance methods on business objects smell really good to me. One less class name I need to remember. However, I don't have a problem with a factory save but I don't see why it would be so difficult to have both. IE

class myObject
{
   public Save()
   {
      myObjFactory.Save(this);
   }
}
...

class myObjectFactory
{
   public void Save(myObject obj)
   {
      // Upsert myObject   
   }
}
Spencer Ruport
+2  A: 

Your domain objects should have no reference to persistance concerns.

Create a repository interface in the domain that will represent a persistance service, and implement it outside the domain (you can implement it in a separate assembly).

This way your aggregate root doesn't need to reference the repository (since it's an aggregate root, it should already have everyting it needs), and it will be free of any dependency or persistance concern. Hence easier to test, and domain focused.

Think Before Coding
+2  A: 

This leads into the Active Record pattern (see P of EAA p. 160).

Personally I am not a fan. Tightly coupling business objects and persistence mechanisms so that changing the persistence mechanism requires a change in the business object? Mixing data layer with domain layer? Violating the single responsibility principle? If my business object is Account then I have the instance method Account.Save but to find an account I have the static method Account.Find? Yucky.

That said, it has its uses. For small projects with objects that directly conform to the database schema and have simple domain logic and aren't concerned with ease of testing, refactoring, dependency injection, open/closed, separation of concerns, etc., it can be a fine choice.

Jason
Exactly, this really just comes down to Active Record vs Domain Model. Each have their place, strengths and weaknesses.
Stefan Moser