Posts Tagged ‘Unit Testing’

Object Oriented Persistence & Repositories

Tuesday, June 8th, 2010

In this post, I will share my own understanding of Object Oriented Persistence and relate it to the Repository pattern, suggested by Eric Evans in Domain Driven Design.

In object oriented perspective, persistence can actually be considered as the extension of the life-cycle of an object. This view of persistence allows us to think that it is actually a minor part of the software, compared to the whole domain. Thus, a programmer’s main focus shouldn’t be the persistence. Although this ideal description of “what should happen” (or what should not happen) sounds neat, this is not what happens most of the time. The reason for that is Database Management Systems are complex entities; they have their own constraints, rules and protocols. If you would like to use a database management system, then you need to follow these rules and constraints.

This clearly is a problem, but there is another, even bigger problem as well. The bigger problem is, in order to follow these constraints, rules, protocols; it is tempting to put the main focus on the database technology & persistence and start doing data processing. Although it sounds bizarre if it is stated like this, this is a contemporary issue and one of the biggest problems in Software Engineering. And more interestingly, even people who claim to be Object Oriented Programmers do that. And I’m not talking about the inexperienced ones, there are people who have been doing this for a long time without knowing it.

In order to avoid this, one needs to know that Object Oriented Programming is not a data processing scheme and persistence is just a minor part of the domain. The word “Domain” is the key here and the solution of this problem lies within the principles of “Domain Driven Design”. (Eric Evans, 2004)

Repositories

Repositories are actually abstract entities that contain a collection of objects. As the programmer, you can put some objects into a repository and then retrieve them back, when they are needed. With this very simple definition of the repositories, they can easily be considered as a part of the domain.

Hence, the client needs to know if he asks for an object in the proper way, the repository will give it to him. Furthermore, the client can also send an object to the repository for further usage. In essence, this is all a client needs to know about a repository.

Looking through the repository’s perspective, the repository both needs to provide a good interface to the client and know how to communicate with the persistence layer.

Persistence Layer / Technology

The implementation details for the persistence layer should not be scattered into the domain layer. In other words, ideally, the client shouldn’t know whether you are using traditional SQL to communicate a relational database, an object database, an ORM Tool or just keeping the objects in a simple array. Independent of those details, the client should be able to communicate with the repository. All those details should be encapsulated within the Persistence Layer. The concrete repositories may need to know which technology you are using. In this sense, repository can be considered both part of the domain and persistence layers.

At this point, it would be better to stop talking about the concepts and do some practice with an example. Suppose that we have a Book class and it has the following class diagram :

Figure 1 : Book Class

Figure 1 : Book Class

Now, suppose we want this entity to become persistent. Then we can create an associated repository, BookRepository class. We need to determine the interface for this class wisely so that we can easily store, retrieve and update books.

BookRepository Interface

Figure 2 : BookRepository Interface

As you see, regardless of the technology behind it, BookRepository interface enables us to store books and retrieve them by various settings.

Although it seems neat, this strategy becomes problematic at some point. In fact, you might have already noticed it easily. We have a simple Book class and we needed 4 retrieval methods. What if Book class was much more complex? This strategy will clearly become problematic because BookRepository might get too crowded.

One solution to this problem is using some “façades” to simplify the BookRepository interface. (please refer to Design Patterns by Gamma et al) However, a more advanced solution would be to use the Specification design pattern. (DDD, Evans) This is not going to be described in this article; it is subject to another article. Hopefully, I will publish an article about specification pattern, soon.

Concrete Repositories

In the example above, we have just given a simple interface for the BookRepository. However, we didn’t describe how a concrete repository looks like. First of all, we need to decide which technology to use. For the sake of simplicity, I choose to use a simple list implementation. In fact, this will not make sense at all because the objects will not be kept in disk. A more advanced and sensible choice would be to use Hibernate. Anyways, the class diagram for BookRepositoryListImpl looks like this :

Figure 3: BookRepositoryListImpl class

Figure 3: BookRepositoryListImpl class

Now, BookRepositoryListImpl can be used as a concrete instance of BookRepository.

List Implementation and Unit Testing

In the above example, I chose list implementation for the sake of simplicity. However, there are also some other places in which the list implementation may come handy, interestingly. For instance, list implementation might become useful if you would like to test a component that uses a repository. Now, assume that we have a class, called BookReservationManager, that has the following definition:

 Figure 4 : BookReservationManager class

Figure 4 : BookReservationManager class

In order to make it more concrete, the implementation of the reserve method is given below:

public Book reserve(String ISBN)
{
   Book b = bookRepository.getByISBN(ISBN);
   if(b.reserve())
      return b;
   return null;
}

If reserve() needs to be unit-tested (and it must be done in isolation, of course) then a mock object of type BookRepository should be used. In fact, the list implementation of the BookRepository can act as a mock object, here. Although BookRepositoryListImpl has nothing to do with the actual implementation, it can be used for unit testing, here. This is beautiful; and the reason for that is if you test reserve() with an actual BookRepository (which uses an actual DBMS) rather than using the list or mock implementation, the failure of one of the database transactions may cause your test to fail.

Summary

To conclude, Repositories are good object oriented and domain-driven ways of implementing persistence. They are part of the domain and this enables us to concentrate on the domain rather than the technology. For each object that needs to be persistent and accessed globally, a corresponding Repository interface should be created. List implementation of a repository allows us to perform unit tests easily on complex components that make use of repositories.

Java Implementation

In this section you will find actual implementation of the example that has been focused on.

Book.java

package com.alicevik.blog.repositories.domain;
  public class Book
  {
 
	private String ISBN;
	private String title;
	private String author;
	private int year;
	private boolean isReserved;
 
	public Book(String ISBN, String title,
        String author, int year)
	{
		this.ISBN = ISBN;
		this.title = title;
		this.author = author;
		this.year = year;
		this.isReserved = false;
	}
 
	public String getISBN() {
		return ISBN;
	}
 
	public String getTitle() {
		return title;
	}
 
	public String getAuthor() {
		return author;
	}
 
	public int getYear() {
		return year;
	}
 
	public boolean reserve()
	{
		if(!isReserved)
		{
			isReserved = true;
			return true;
		}
 
		return false;
	}
 
	public void read()
	{
		// left blank intentionally.
                // irrelevant for this example.
	}
  }

BookRepository.java

package com.alicevik.blog.repositories.domain;
 
public interface BookRepository {
 
	void store(Book b);
	Book getByISBN(String ISBN);
	Book[] getByTitle(String title);
	Book[] getByAuthor(String author);
	Book[] getBooksBetwYears(int year1, int year2);
}

BookRepositoryListImpl.java

package com.alicevik.blog.repositories.domain;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
 
/* List implementation of the BookRepository interface.
 * Author : Ali Çevik
 * Only the methods of interest are implemented.
 * 08.06.2010
 */
 
public class BookRepositoryListImpl implements BookRepository
{
 
	private List list;
 
	public BookRepositoryListImpl()
	{
		list = new ArrayList();
	}
 
	@Override
	public Book[] getBooksBetwYears(int year1, int year2)
	{
		return null;
	}
 
	@Override
	public Book[] getByAuthor(String author)
	{
		return null;
	}
 
	// iterates through the list to find the match
	@Override
	public Book getByISBN(String ISBN)
	{
		Book book = null;
		Iterator it = list.iterator();
		while(it.hasNext())
		{
			Book b = it.next();
			if(b.getISBN() == ISBN)
				book = b;
		}
		return book;
	}
 
	@Override
	public Book[] getByTitle(String title)
	{
		return null;
	}
 
	@Override
	public void store(Book b)
	{
		list.add(b);
	}
 
}

BookReservationManager.java

package com.alicevik.blog.repositories.domain;
 
public class BookReservationManager
{
	private BookRepository bookRepository;
 
	public BookReservationManager(BookRepository repository)
	{
		bookRepository = repository;
	}
 
	public Book reserve(String ISBN)
	{
		Book b = bookRepository.getByISBN(ISBN);
 
		if(b.reserve())
			return b;
 
		return null;
	}
 
}

BookReservationManagerTest.java

package com.alicevik.blog.repositories.test;
 
import junit.framework.TestCase;
 
import com.alicevik.blog.repositories.domain.Book;
import com.alicevik.blog.repositories.domain.BookRepository;
import com.alicevik.blog.repositories.domain.BookRepositoryListImpl;
import com.alicevik.blog.repositories.domain.BookReservationManager;
 
public class BookReservationManagerTest extends TestCase {
 
	private BookRepository repository;
	public void setUp()
	{
		repository = new BookRepositoryListImpl();
		repository.store(new Book("1111-1111-1111", "Faust", "Goethe", 1832));
	}
 
	public void testReserve()
	{
		BookReservationManager man = new BookReservationManager(repository);
		assertNotNull(man.reserve("1111-1111-1111"));
		assertNull(man.reserve("1111-1111-1111"));
	}
 
}