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();
 }
}


About this entry