Environmental domain modelling

In my last post, I talked about how useful building a domain model of your technical environment can be in agile system integration projects.

The argument goes something like this:
  1. There are architectural boundaries in your project – e.g. a web UI server, a SOAP/rest application server, a mainframe at the end of a message queue.
  2. You are building in quality and reducing defects by implementing automated acceptance tests.
  3. You run these automated acceptance tests locally, but also in different phases of the release process (e.g. after the unit test CI build, running with multiple browsers).
  4. In each set of tests the environment is subtly different – web servers have different urls, some systems are simulated rather than real, etc.
  5. Rather than have arbitrary configuration parameters for each part of the system, instead build a domain model that encapsulates the relationships between deployable units.
So, let’s start with a simple environment:
trait Environment {
 URI getWebUILocation();
 URI getBusinessServiceLocation();
 bool requireWebUI();
 bool requireBusinessService();
 void stop();
 void kill();
}
class InMemoryLocalEnvironment implements Environment {
 //Jetty
 Server server = ...
 String uiWebApp = "..."; 
 SimulatedBusinessService simulatedBusinessService = new SimulatedBusinessService();
 int port = 8080;
 bool requireWebUI() {
   startServerIfNotStarted()
   server.addWebApplication( "web-ui-context", uiWebApp ) 
 } 
 protected void startServerIfNotStarted() {  server.start(); }
 bool requireBusinessService() {  server.addHandler ( "serviceName", simulatedBusinessService()); }  
 URI getWebUILocation() { return new URI( "http://localhost:" + port + "/web-ui-context" ) }
 URI getBusinessServiceLocation() { return new URI( "http://localhost:" "+ port + "/serviceName" ) }
  public void setWebUIPath( String uiApplication ) {
  ...
 }
}

This may need a bit of explanation. Firstly, you have an interface (Environment) to your environment. At it’s most general, it provides accessors for locating various services (needed during acceptance testing), and a mechanism of requiring that such services exist. In this case, I’m expecting a based web site which accesses some external REST or soap server.

Secondly, there is the first implementation of your environment class – InMemoryLocalEnvironment.  The name here is trying to indicate that you use this enviroment if you want something quick to instantiate, and don’t want to use network versions of the service.

This InMemoryLocalEnvironment uses Jetty internally to start the web app under development, and to use another web app to host a simulated business service.  The details of the simulator are out of scope here, but you can imagine it has stock responses to requests based on well known input data (eg. deny all applications from “Dr. Evil”).

During an acceptance test run, I may want to point selenium RC at my web app.  Rather than hardcode it into the acceptance test itself, I may do the following (using the RSpec story runnner under JRuby):

Given( 'an anonymous user on web UI' ) do
  environment.requireWebUI()
  selenium.open( environment.getWebUILocation )
end
...
Then ('a new user exists' ) do
  environment.requireBusinessService()
  selenium.open( "#{environment.getBusinessServiceLocation()}/users/#{new-user-id}
end 

So far so good – this is simple indirection for run time configuration – you can do the same thing with property files, except this time, your tests don’t have to know how to create the right environment – it’s all decided before the tests run.

Now let’s create another environment:

class UatEnvironment implements Environment {
 URI getWebUILocation() { new URI( "http://uat1.myco/myui" ); }
 URI getBusinessServiceLocation() { new URI( "http://uat2.myco/businessService/" ); }
 bool requireWebUI() { //ping UAT environment... };
 bool requireBusinessService() { //ping UAT environment... };
 void stop() {};
 void kill() {}
}

In this case, the requirements are that an existing application has already been uploaded. We have built in checks to make sure that the web ui and business services exist prior to running the tests. All the acceptance tests (that require a particular dependency) will pass.

Again all this can be done with property files and an anaemic domain model. However, I’ve used this model to build clarity around the design of the application, which sometimes gets lost in the thrust to cut code.

Of course, there must be some extra value, over and above the property file mechanism. I’ll talk about later…

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: