Service Architecture Framework

The Service Architecture Framework or SAF describes a simple implementation in Java of a publish-subscribe mechanism for domain objects. This is a simple and effective strategy to implement a plugin-like architecture for domain driven design. We have written about Business Centred Architectures (in Dutch: Business Centred Architecturen) in which you could see some examples using Smalltalk.

There is example Java code available on GitHub: https://github.com/robject/saf-service-framework

Project overview

This is the source code structure from the repository referred to above:

All classes are commented according to the javadoc styles.

The adapter package contains the relevant classes, with two packages in it:

  1. examples — this contains example classes to show how to use the framework with your own domain classes
  2. tests — the JUnit tests for the framework classes
We created a special exception class, which as you can see is placed in the exceptions package.

Framework Overview

Below you can find the overview UML class diagram of the framework.

As you can see in the picture above, the base of the framework consists of an observable – observer pair, as is customary in all existing MVC-like architectures.

All your domain classes are supposed to inherit from ChangingObservable, which is a slightly modified standard java.util.Observable class. This is a prerequisite that might impose too much on your existing classes — Java is a single inheritance language and you may not be able to subclass your existing domain classes from this class. In that case you will need to explore other strategies.

The IValue interface class contains the interface your observer classes need to conform to. This requirement will impose no restrictions on your code.

The basic idea behind the framework can be summarised as follows:

  1. Domain objects are ChangingObservables, containing the minimal code to function as such: your domain objects only send setChanged() to themselves. They do this anytime something happens that corresponds to an internal state change. The method is implemented in the superclass of course.
  2. Observers can subscribe themselves to events from the domain objects — however the code that does this is not supposed to be written by the application developers. It is part of the framework, and we will show how these connections are made.
  3. Technical components are never directly linked to from the domain objects: the events fired by the domain objects are caught by one or more Adapters, and propagated to the technical component. Examples of technical components are GUI elements (as in the original MVC), but may just as well be persistence connectors (such as Hibernate), logging components, etc.

The result is a business domain that is almost perfectly isolated, to be maintained and extended in isolation by a dedicated developers group, in close cooperation with the business users and experts themselves, or product owners if you use scrum.

Time for an example. Say we have a domain class named Person:

package reflektis.saf.adapter.examples;

import reflektis.saf.adapter.ChangingObservable;

/**
* @author Rob Vens
* @version 1.0
* @created 28-May-2005 14:40:57
*/
public class Person extends ChangingObservable {

  /**
  * The name of the person.
  * Initialize to an empty string.
  */
  private String name = "";

  /**
  * The address of the person.
  */
  public Address m_Address;

    public void finalize() throws Throwable {
    super.finalize();
  }

  public Person() {

  }

  /**
  * @return Returns the name.
  */
  public String getName() {
  return name;
  }

  /**
  * @param newName
  * The name to set.
  */
  public void setName(String newName) {
  this.name = newName;
  this.setChanged("name");
  }

  /**
  * @return m_Address.
  */
  public Address getAddress() {
  return m_Address;
  }

  /**
  * @param m_Address
  * The address to set.
  */
  public void setAddress(Address newAddress) {
  this.m_Address = newAddress;
  this.setChanged("address");
  }
}

Nothing fancy about this class, it is a vanilla implementation of a domain class. Notice that the only places you can see that this domain class is a bit different are the sendings of setChanged() to themselves, in lines 44 and 60. This method, implemented in a superclass, eventually sends notifyObservers() to a collection of objects that at some time in the past have registered themselves as such with the domain object. This line of code is the only thing developers of domain objects need to do, every time something happens in a domain object that can be interpreted as a state change. That is all. Once these hooks are in place, everything from persistence, logging, user interface linking and so forth will be taken care of. Note that domain class developers never do anything with these observers directly! They only send a message to themselves.

Remember: the goal was to make it possible for domain modelling and implementation to be done in relative isolation, with a dedicated group of developers, focussing on delivering the domain functionality. The way these domains should be created need to give less attention to possible user interfaces than is usually the case. User interfaces should be seen as technical components, like views into the domain offering more or less handles to touch (and possible change) the domain objects.

Of course, the interesting part (at least for the purpose of this article, domain modelling is certainly interesting enough in itself!) is what happens before and after.

Before: how do observers get registered with the domain objects?

After: how do change events from the domain object get propagated to technical components, so that user interfaces stay in sync, events are logged, changes to domain objects are persisted in a database?

The answer to both is that this should be taken care of by the framework, and not by code written and maintained by developers in projects. Let’s zoom in on this a bit more.


This is the update method in the class AspectAdapter:


/**
* Test for value strings here and only update observers when the argument
* indicates that we get an update of the aspect I am interested in.
* Creation date: (10-5-2001 17:26:01)
*
* @param sender
* the object that wants to notify its observers
* @param anAspect
* argument containing info on the kind of change
*/

public final void update(final Object sender, final Object anAspect) {
  if ((sender == subject && anAspect.equals(this.aspect))) {
    // make sure the changed flag is set
    // otherwise the notification is not done
    this.setChanged();
    this.notifyObservers(anAspect);
  } else {
    // effectively no-op
    super.update(sender, anAspect);
  }
}

 

The method above is the method that must be implemented by the technical services that want to subscribe to events from the domain objects. In this case this is an adapter that is created and parameterised to listen to a specific change event in the domain. For example, let’s assume we have a persistency adapter listening to the address aspect of a Person object.

Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.


Copyright © 2019, reflektis & Rob Vens