Archive for the ‘persistence’ Category.

Domain Driven Design, The Repository pattern

I’ve seen so many times a heated discussion about Repositories in Domain Driven Design, there is a kind of misunderstanding and/or confusion about what really a Repository is.

People usually disagree about the role of the Repository, if it should be a kind of  “Data Access Layer”, such as a DAO or if it must be something that is injected in the domain class, then it will talk to the DAO in the beneath layer.

I’ve always wondered why people had these fights, but now I can see why ! The thing is, as far as I read in the book (Chapter 10 : Supple Design),  the examples of Reposotory has been shown almost exactly as a DAO implementation. I even downloaded the source code examples from the Domain Driven Design Community, and the example IS a DAO implementation, as I’m showing below :

package se.citerus.dddsample.domain.model.cargo;

import java.util.List;

public interface CargoRepository {

  /**
   * Finds a cargo using given id.
   *
   * @param trackingId Id
   * @return Cargo if found, else {@code null}
   */
  Cargo find(TrackingId trackingId);

  /**
   * Finds all cargo.
   *
   * @return All cargo.
   */
  List findAll();

  /**
   * Saves given cargo.
   *
   * @param cargo cargo to save
   */
  void store(Cargo cargo);

  /**
   * @return A unique, generated tracking Id.
   */
  TrackingId nextTrackingId();

}

and the implementation

package se.citerus.dddsample.infrastructure.persistence.hibernate;

import org.springframework.stereotype.Repository;
import se.citerus.dddsample.domain.model.cargo.Cargo;
import se.citerus.dddsample.domain.model.cargo.CargoRepository;
import se.citerus.dddsample.domain.model.cargo.TrackingId;

import java.util.List;
import java.util.UUID;

/**
 * Hibernate implementation of CargoRepository.
 */
@Repository
public class CargoRepositoryHibernate extends HibernateRepository implements CargoRepository {

  public Cargo find(TrackingId tid) {
    return (Cargo) getSession().
      createQuery("from Cargo where trackingId = :tid").
      setParameter("tid", tid).
      uniqueResult();
  }

  public void store(Cargo cargo) {
    getSession().saveOrUpdate(cargo);
    // Delete-orphan does not seem to work correctly when the parent is a component
    getSession().createSQLQuery("delete from Leg where cargo_id = null").executeUpdate();
  }

  public TrackingId nextTrackingId() {
    // TODO use an actual DB sequence here, UUID is for in-mem
    final String random = UUID.randomUUID().toString().toUpperCase();
    return new TrackingId(
      random.substring(0, random.indexOf("-"))
    );
  }

  public List findAll() {
    return getSession().createQuery("from Cargo").list();
  }

}

This Repository is used by a class called BookingServiceImpl :

package se.citerus.dddsample.application.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.transaction.annotation.Transactional;
import se.citerus.dddsample.application.BookingService;
import se.citerus.dddsample.domain.model.cargo.*;
import se.citerus.dddsample.domain.model.location.Location;
import se.citerus.dddsample.domain.model.location.LocationRepository;
import se.citerus.dddsample.domain.model.location.UnLocode;
import se.citerus.dddsample.domain.service.RoutingService;

import java.util.Collections;
import java.util.Date;
import java.util.List;

public final class BookingServiceImpl implements BookingService {

  private final CargoRepository cargoRepository;
  private final LocationRepository locationRepository;
  private final RoutingService routingService;
  private final Log logger = LogFactory.getLog(getClass());

  public BookingServiceImpl(final CargoRepository cargoRepository,
                            final LocationRepository locationRepository,
                            final RoutingService routingService) {
    this.cargoRepository = cargoRepository;
    this.locationRepository = locationRepository;
    this.routingService = routingService;
  }

  @Override
  @Transactional
  public TrackingId bookNewCargo(final UnLocode originUnLocode,
                                 final UnLocode destinationUnLocode,
                                 final Date arrivalDeadline) {
    // TODO modeling this as a cargo factory might be suitable
    final TrackingId trackingId = cargoRepository.nextTrackingId();
    final Location origin = locationRepository.find(originUnLocode);
    final Location destination = locationRepository.find(destinationUnLocode);
    final RouteSpecification routeSpecification = new RouteSpecification(origin, destination, arrivalDeadline);

    final Cargo cargo = new Cargo(trackingId, routeSpecification);

    cargoRepository.store(cargo);
    logger.info("Booked new cargo with tracking id " + cargo.trackingId().idString());

    return cargo.trackingId();
  }

  @Override
  @Transactional
  public List requestPossibleRoutesForCargo(final TrackingId trackingId) {
    final Cargo cargo = cargoRepository.find(trackingId);

    if (cargo == null) {
      return Collections.emptyList();
    }

    return routingService.fetchRoutesForSpecification(cargo.routeSpecification());
  }

  @Override
  @Transactional
  public void assignCargoToRoute(final Itinerary itinerary, final TrackingId trackingId) {
    final Cargo cargo = cargoRepository.find(trackingId);
    if (cargo == null) {
      throw new IllegalArgumentException("Can't assign itinerary to non-existing cargo " + trackingId);
    }

    cargo.assignToRoute(itinerary);
    cargoRepository.store(cargo);

    logger.info("Assigned cargo " + trackingId + " to new route");
  }

  @Override
  @Transactional
  public void changeDestination(final TrackingId trackingId, final UnLocode unLocode) {
    final Cargo cargo = cargoRepository.find(trackingId);
    final Location newDestination = locationRepository.find(unLocode);

    final RouteSpecification routeSpecification = new RouteSpecification(
      cargo.origin(), newDestination, cargo.routeSpecification().arrivalDeadline()
    );
    cargo.specifyNewRoute(routeSpecification);

    cargoRepository.store(cargo);
    logger.info("Changed destination for cargo " + trackingId + " to " + routeSpecification.destination());
  }

}

Well, I cannot deny that I did not dig so deep into the code ( and I did not finish the book ) yet,
but what I can state for sure is that this architecture really looks like this sequence :

Repository Sequence Diagram
Also, the layering :

Layering Repository Diagram
Ok, I know I am over simplifying things here, but my intention is only to communicate my point of view about
the topic. I’m not saying that what is inside this code is wrong at all, I am just skeptical about how “new” or
effective this thing is, because I’ve been developing large scale software systems using the same approach, but with a
different name “DAO”, and so far I could not see anything different from this. I might let you guys twist my arms in the end of the book,
but so far I’m pretty sure the Repository and a DAO pattern are the same thing.

Any idea, suggestions, thoughts and counter-argument are very welcome ! :)

Will a persistence framework always be enough ?

Indeed, persistence is always a nice topic to discuss, and I’d like to bring here more one round about this so nice subject.

In the early Java days, we used to create our persistence layer programatically, we simply used an Active Record design pattern ( or something closer to it) to make our classes “persistable”. Some time later, we introduced the DAO design pattern inside our applications, creating an abstraction to separate our “dirty work” to access any data resource, in this case, most common a database connection, and CRUD operations.

Nowadays, we’ve seen the advent of Object-Relational mapping tools, such as Hibernate, TopLink, Kodo, among many others. Nobody can deny that these tools are becoming better and richier every day, adding new features, development plugins, database support and so on. The best advantage of using them is the bridge they make between our application to the database, solving or trying to solve the well known object-relational impedance mismatch problem. Besides that they have some mechanisms that facilitate our lives as developers. I can point out Hibernate features :

  • Transparent Persistence
  • Flexible Mapping
  • Query Facilities
  • Metadata Facilities, among others.

You can know more looking into Hibernate’s website.

In opposition what people tend to believe, an object-relational mapping tool will be enough when we won’t need some in depth database specific capability. But, sometimes we need more. Depending on what kind of system we are dealing with, we’ll need care more about performance, we may have some legacy database to deal with, there might have some company policy that forces us to access data through stored procedures, or issue some special SQL statement, a PL/SQL block, a Transact-SQL block, or something related.

In these cases, I’d like to stand against the myth about portability between databases !

As I told before, there are people who believe that a persistence framework will shield their applications about every single database system, or evey every single different problem found in an Enterprise Software. That’s indeed a really wrong ideia about enterprise software.

Instead, I do prefer to give an appropriate solution for each case. The the role of any software developer, is to choose the most suitable solution for a given problem. We should not limit everything to a single solution, doing so, we might be making things more difficult to solve than it should be.

It’s not a sin to use a separate DAO and make JDBC calls, and it WON’T prevent you to migrate your application to another database. Of course a dose of common sense, analysis and good design are always welcomed. There are design patterns and guidelines to help us in how to develop a better data access layer using both a persistence framework and plain sql/jdbc access, and the effort to migrate your application won’t be a big deal, as long as you didn’t build your entire software inside your database.

I consider two nice actions dealing with this situation :

  1. Keep any specific sql inside your database ( as a stored procedure ). Doing so,you’ll have a centralized repository, you can have your sql tested and validated for free, instead of keeping strings inside your application, that can change and break your functionality.
  2. Issue any batch operation to your database, because in most cases it’s is easier and cheaper to call a procedure/function to query and calculate something ( that is already there in the database), rather than try to load this data on a distributed environment, passing through a network, and summarize your information in memory.

So, my answer is NO, there are situations where a persistence framework won’t be enough to solve your problems. You should bear in mind that there is more than one way to solve a problem, and keep your view as pragmatic as possible, in order to don’t limit your solution to a single vendor / product / architecture / etc.

In this first post, I provided some ground about this discussion, there are a lot more to discuss about this subject, and I will be posting more about this sooner. ;)