Sometimes dependency inversion does not give full flexibility. The abstract factory pattern may then prove to be useful when building maintainable software. At least it allowed me to keep my single threaded persistence fake for testing in an environment that otherwise required multithreaded transactions... and this is how...

I recently wrote a post about how I use dependency inversion and fakes to write better code. However, that solution does not work in a multithreaded environment that requires multiple persistence operations for each request. This article is a follow up article that aims to solve above stated problem.

The Problem

For a while I have defined an interface to inverse dependencies on the persistence layer. The basic idea is an interface defines method that can be used to communicate with a persistence layer and that the actual persistence layer can be substituted without effecting the rest of the application. This has been a successful strategy for a  long time, but new requirements on the system has forced me to redesign the whole concept.

The problem is that I want the flexibility the above solution gives me but in a complex multithreaded environment where multiple persistence operations can be executed in a all or nothing manner. To complicate things further, there is no guarantee that the same thread will execute all persistence operations.

The obvious solution to multiple persistence requests is to use ACID transactions. However, transactions require some sort of initialisation to create a context on which the operations are performed before they are committed. Adding transaction specific operations to the persistence interface would force all implementations of the persistence layer to implement transactions, event those implementations which do not support transactions, such as the in-memory fakes used in development and testing.

Even though, transactions has some problems the other alternative of a two phase commit is even worse, since it would require custom implementation of commits and rollbacks that you get for free with transactions.

The Solution

The solution is to define an Abstract Factory that generates short lived objects on which multiple persistence operations can be executed. These objects will capsulate any transaction specific context so that only the persistence operations visible to the owner of the object. The operations can then be performed on the object and  automatically committed when the object destructed (if you are using a language that does not support destructors, such as JavaScript, you may define a commit function in your interface which can be called after the last operation).

A more detailed explanation follows below together with a schematic sketch that can be found in figure 1, where Service is the part of the application responsible for handling business logic.

Figure 1. Creating Persistors with an Abstract Factory

The Persistor

The object responsible for communicating with the persistence layer and on which persistence operations are performed is called a Persistor and it implements the Persistor Interface. The Persistor Interface defines methods that Service can use for persistence. This separation between the usage and its implementation of a Persistor, ensures the Persistor can hold implementation specific context without effecting the Service. (In figure 1 there are two different implementations of the Persistor Interfaces, these two implementations can be substituted for one another depending on situations, e.g. test or production).

A Persistor can now be passed around to all places where operations on the transaction are performed, provided that objects are passed by reference.

The Persistor Factory

The second interface used by Service is the PersistorFactory. This interface defines factory methods for creating persistor objects.

A reference to a PersistorFactory is given to Service at initialization by dependency injection. Whenever Service needs to perform some persistence operations it calls the PersistorFactory to receive a newly created Persistor.

The reason for the PersistorFactory is to ensure that Service does not know anything about the creation of Persistors. Without the PersitorFactory, implementation specific code for creating the Persistors would leak in to Service, making it dependent on a specific Persistor implementation.

With all in place, it is now easy to switch the persistence layer between a simple in-memory fake and a production ready implementation that supports transactions.

Some Final Take Aways

In figure 1, everything outside the grey box is part of the same module, making this module the owner of both the PersistorFactory Interface and the Persistor Interface. Because of this ownership, there is no dependency on the implementations within the grey box.

Everything inside the grey box can either be part of the same module or separated in to multiple modules as long as they implement the Persistor Interface and the PersistorFactory Interface.

Note also, that all arrows crossing the grey box points outwards indicating that grey box can be substituted for other implementations and that Service knows nothing about the existence of the grey box but assumes there is one.

Pseudo Code

Finally, here is some pseudo code to show how it all works.

//////// OUTSIDE THE GREY BOX //////////

public interface Persistor {
    func fetch() -> Type
}

public interface PersistorFactory {
    func createPersistor() -> Persistor
}

class Service {
    PersistorFactory factory;
    
    init(factory: PersistorFactory) {
        self.factory = factory
    }
    
    func fetch() -> Type {
        Persistor persistor = self.factory.createPersistor()
        
        return persistor.fetch()
    }
}

//////// INSIDE THE GREY BOX //////////

class ConcretePersistorFactory implements PersistorFactory {
    
    func createPersistor() -> Persistor {
        return new ConcretePersistor()
    }
}

class ConcretePersistor implements Persistor {
    
    func fetch() -> Type {
        // Do some real stuff
        return new Type()
    }
}


//////// INITIALIZATION //////////

ConcretePersistorFactory factory = ConcretePersistorFactory()
Service service = Service(factory: factory)