Fork me on GitHub

Activity Tutorial

This tutorial demonstrates modularising configuration.

To focus on modular configuration, this tutorial uses a simple application that returns the depth of modules within modules.

Tutorial Source

Background

Modularising configuration is necessary when working on bigger projects by bigger teams.

Trying to configure all logic of a large application into the single configuration file yields a lot of complexity. Breaking configuration into smaller files reduces risk of merge conflicts and makes it easier to understand.

Also, scaling teams requires developers to work in isolated parts of the applications. Having the single configuration file creates a focal point for changes. This causes developers to step on each other's toes and creates problems through increased number of merge conflicts. Breaking configuration into smaller files reduces risk of merge conflicts.

By modularising the configuration to smaller files, it allows:

  • developers to work in isolation of each other
  • lower chances of merge conflicts
  • easier to comprehend configuration
  • less responsibilities for individual configuration files
  • and many other benefits

Activity

Modularising the configuration is undertaken with the ActivitySectionSource. The name Activity comes from business. An Activity is a composition of many Procedures.

The application must add support via the following dependency:

		<dependency>
			<groupId>net.officefloor.activity</groupId>
			<artifactId>officeactivity</artifactId>
		</dependency>

To demonstrate using Activities to modularise configuration, the REST endpoint YAML configures the top-level flow:

initiate:
  class: net.officefloor.tutorial.activityhttpserver.InitiateDepthLogic
  method: initiate
  next: LevelOne
LevelOne:
  source: net.officefloor.activity.ActivitySectionSource
  location: LevelOne.activity
  outputs:
    Output: send
send:
  class: net.officefloor.tutorial.activityhttpserver.SendDepthLogic
  method: send

The above configuration has a Procedure:

	public Depth initiate() {
		return new Depth(0);
	}

creating and passing on the following parameter:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Depth {

	private Integer level;
}

to the following modular Activity (LevelOne.activity).

Within the Activity, the Procedure increments the depth:

	public Depth increase(@Parameter Depth depth) {
		return new Depth(depth.getLevel() + 1);
	}

that is passed onto a further modularised Activity (LevelTwo.activity) that also increases the depth.

The result is then passed out of the modularised Activity configurations to the top level WoOF configuration to send the result via the following Procedure:

	public void send(@Parameter Depth depth, ObjectResponse<Depth> response) {
		response.send(depth);
	}

Complex Modularising

This is only a simple example of modularising configuration.

More complex Activity configurations may have multiple inputs and outputs. Furthermore, they may internally provide exception handling (or let exceptions propagate to a higher Activity).

However, only the inputs/outputs are shown to higher level configuration. This encapsulates the modularised Activity so developers can change without impacting higher level configurations. This is akin to coding behind interfaces (i.e. configuring behind input/output interfaces).

Furthermore, as the Activity is loaded from the class path, Activities may be built as libraries. Applications can then import the resulting built jars to re-use the modularised Activity functionality.

Testing

The following tests the increased depth of configuration modularisation:

	@RegisterExtension
	public MockWoofServerExtension server = new MockWoofServerExtension();

	@Test
	public void correctDepth() throws Exception {
		MockWoofResponse response = this.server.send(MockWoofServer.mockRequest());
		response.assertJson(200, new Depth(2));
	}

Next

The next tutorial covers transaction (GovernanceSource) within a WoOF web application.