December 10, 2004

Separate Domain and Persistence Layer

Ade completed the example of a separate domain and persistence layer. It uses xstream for persistence.

Example below:

Repository example:

import junit.framework.TestCase;

import java.util.List;

public class DepartmentTest extends TestCase {
public void testAddedEmployeesCanBeListed() {
Department sales = new Department("Sales");
Employee johnSmith = new Employee("John Smith");
sales.add(johnSmith);
Employee janeSmith = new Employee("Jane Smith");
sales.add(janeSmith);

List employees = sales.getAllEmployees();
assertEquals(2, employees.size());
assertTrue(employees.contains(johnSmith));
assertTrue(employees.contains(janeSmith));
}

public void testEmployeeCanBeTransferredFromOneDepartmentToAnother() {
Department sales = new Department("Sales");
Employee johnSmith = new Employee("John Smith");
sales.add(johnSmith);

Department marketing = new Department("Marketing");
sales.transfer(marketing, johnSmith);

List marketingEmployees = marketing.getAllEmployees();
assertEquals(1, marketingEmployees.size());
assertTrue(marketingEmployees.contains(johnSmith));

//confirm john's left the sales dept
List salesEmployees = sales.getAllEmployees();
assertEquals(0, salesEmployees.size());
assertFalse(salesEmployees.contains(johnSmith));
}

public void testPersistedDepartmentCanBeRetrieved() {
String name = "Sales";
Department sales = new Department(name);
Employee johnSmith = new Employee("John Smith");
sales.add(johnSmith);
Employee janeSmith = new Employee("Jane Smith");
sales.add(janeSmith);

DepartmentRepository repository = new DepartmentRepository();
repository.add(sales);

Department retrievedSalesDept = repository.findByName(name);
assertEquals(sales, retrievedSalesDept);

List employees = retrievedSalesDept.getAllEmployees();
assertEquals(2, employees.size());
assertTrue(employees.contains(johnSmith));
assertTrue(employees.contains(janeSmith));
}
}

================

import com.thoughtworks.xstream.XStream;

import java.io.FileWriter;
import java.io.IOException;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.File;
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;

public class DepartmentRepository {
private static final String FILE_NAME = "departmentRepository.xml";
private XStream xstream;

public DepartmentRepository() {
this.xstream = new XStream();
}

public void add(Department dept) {
try {
List departments = getExistingDepartments();
departments.add(dept);
xstream.toXML(departments, new FileWriter("departmentRepository.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}

private List getExistingDepartments() throws FileNotFoundException {
File file = new File(FILE_NAME);
if (file.exists()) {
return (List) xstream.fromXML(new FileReader(FILE_NAME));
}
return new ArrayList();
}

public Department findByName(String name) {
XStream xstream = new XStream();
try {
List departments = getExistingDepartments();
for (Iterator iterator = departments.iterator(); iterator.hasNext();) {
Department department = (Department) iterator.next();
if (department.getName().equals(name)) {
return department;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}

return null;
}
}
===================================

public class Employee {
private String name;

public Employee(String name) {
this.name = name;
}

public boolean equals(Object object) {
Employee other = (Employee) object;
return this.name.equals(other.name);
}
}
========================================

import java.util.ArrayList;
import java.util.List;

public class Department {
private String name;
private List employees;

public Department(String name) {
this.name = name;
this.employees = new ArrayList();
}

public void add(Employee employee) {
employees.add(employee);
}

public List getAllEmployees() {
return employees;
}

public void transfer(Department other, Employee employee) {
this.employees.remove(employee);
other.employees.add(employee);
}

public String getName() {
return name;
}

public boolean equals(Object object) {
Department other = (Department) object;
return this.name.equals(other.getName());
}
}

Posted by chrismatts at December 10, 2004 3:55 PM
Comments

So, to cleanly separate domain code from persistence, you need to use meta-programming techniques. E.g., to persist an object that represents a domain concept you have to obtain a meta-object that represents the representation of the domain concept, and then process the meta-object.

Posted by: Nat Pryce at December 10, 2004 5:28 PM

The alternative (to meta-programming techniques) involves explicitly exposing all of a domain object's state by having a getter for each persistent property. Which is fine for some applications especially if you take steps to make sure it isn't abused.

But meta-programming (via reflection, Self-style Mirrors or even an explicit domain object descriptor file) is probably your best bet if you want to separate the domain from your persistence layer.
Anything else ends up creating a more or less explicit dependency between your domain object and your persistence mechanism.

Posted by: ade at December 10, 2004 8:41 PM
Post a comment









Remember personal info?