Play!> framework design pattern #3 : Avoid Anemic Domain Model

In my last post about data integrity, I noticed that with the MVC model of Play, you tend to put many logic in the controller, even a part of the domain model, thus creating an Anemic Domain Model as described by Martin Fowler.

And it’s what I made in my Codebaord Project to test Play! Framework. In my model, I have a class Domain that is only a database wrapper (with only annotations), and a controller Domains that has code that should be in the model (see old controller source with this wrong choice).

So, I started replace domain code in the domain classes.

Now the controller class is lighter :

public class Domains extends Admin \{

public static void list() \{

List<Domain> domains = Domain.find("order by position asc").fetch();

render(domains);

}

public static void create() \{

Domain domain = new Domain();

render("@edit", domain);

}

public static void edit(Long id) \{

Domain domain = Domain.findById(id);

render(domain);

}

public static void setDefault(Long id) \{

Domain domain = Domain.findById(id);

domain.setDefault();

Map summary = new HashMap();

summary.put(0, 1);

summary.put(1, Messages.get("domain_as_default"));

summary.put(2, id);

renderJSON(summary);

}

public static void save(@Valid Domain domain) \{

if (Validation.hasErrors()) \{

params.flash(); // add http parameters to the flash scope

validation.keep(); // keep the errors for the next request

render("@edit", domain);

}

domain.save();

list();

}

public static void delete(Long id) \{

Domain domain_to_delete = Domain.findById(id);

Map summary = new HashMap();

try \{

domain_to_delete.delete();

summary.put(0, 1);

summary.put(1, Messages.get("domain_deleted"));

summary.put(2, domain_to_delete.id);

} catch (UnsupportedOperationException e) \{

summary.put(0, 0);

summary.put(1, Messages.get(e.getMessage()));

summary.put(2, domain_to_delete.id);

}

renderJSON(summary);

}

public static void move(Long id) \{

String to = params.get("move_to");

Domain domain_to_move = Domain.findById(id);

domain_to_move.move(to);

Map summary = new HashMap();

summary.put(0, Messages.get("domain_moved"));

summary.put(1, domain_to_move.id);

summary.put(2, domain_to_move.position);

renderJSON(summary);

}

}

and the model class is no more "empty" :

@Entity

public class Domain extends Model \{

public String name;

public long position;

public boolean isPublic;

public boolean isDefault;

public String toString() \{

return name;

}

public Domain delete() \{

if (! isDefault) \{

super.delete();

} else \{

throw new UnsupportedOperationException("dont_delete_default");

}

List<Domain> domains = Domain.find("position > ?", position).fetch();

for (Iterator iterator = domains.iterator(); iterator.hasNext(); ) \{

Domain domain = (Domain) iterator.next();

domain.position = domain.position - 1;

domain.save();

}

return this;

}

public Domain setDefault() \{

Domain defaultDomain = Domain.find("isDefault = ?", true).first();

if (defaultDomain != null) \{

defaultDomain.isDefault = false;

defaultDomain.save();

}

isDefault = true;

save();

return this;

}

public void move(String to) \{

if (to.equals("highest")) \{

List<Domain> domains = Domain.find("position < ?", position).fetch();

for (Iterator iterator = domains.iterator(); iterator.hasNext();) \{

Domain domain = (Domain) iterator.next();

domain.position = domain.position +1;

domain.save();

}

position = 1;

} else if (to.equals("higher")) \{

Domain domain_to_swap = Domain.find("position = ?", position - 1).first();

domain_to_swap.position = position;

position = position - 1;

domain_to_swap.save();

} else if (to.equals("lower")) \{

Domain domain_to_swap = Domain.find("position = ?", position + 1).first();

domain_to_swap.position = position;

position = position + 1;

domain_to_swap.save();

} else if (to.equals("lowest")) \{

List<Domain> domains = Domain.find("position > ?", position).fetch();

for (Iterator iterator = domains.iterator(); iterator.hasNext();) \{

Domain domain = (Domain) iterator.next();

domain.position = domain.position - 1;

domain.save();

}

position = Domain.count();

}

save();

}

}

[/code]

comments powered by Disqus