Skip to content

Commit

Permalink
Add existing decisions from arch-as-code repo
Browse files Browse the repository at this point in the history
  • Loading branch information
sldblog committed Dec 15, 2020
1 parent fab2922 commit c2eac3b
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# hmpps-interventions-docs

Decisions and other shared documentation that spans across all applications in Interventions.

- [Architecture decisions](doc/adr/)
79 changes: 79 additions & 0 deletions doc/adr/0001-split-ui-and-service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# 1. Split user and business interfaces by default

Date: 2020-11-02

## Status

Accepted

## Context

### Need for sustainable services

The Ministry of Justice 2022 Digital Strategy defines "Building sustainable services" as a priority.

As the domain model of HM Prisons and Probation Service (HMPPS) is big, we aim to build sustainable services
around [bounded contexts][bounded-context], making it necessary to integrate with other contexts.

### Domain and business logic

Historically, many systems built in HMPPS did not expose an API for the business logic and/or were managed by third
parties, leading to difficult integration: we see business APIs beneficial to have from the start.

Existing APIs that are thin layers over a database (entity services) are easy to write but hard to use as it pushes
the need to handle business logic back to the clients.

We have seen services with a frontend/backend split where frontend clients accumulated business logic as the
backend service fell back to provide a CRUD entity service over the database; this is not sustainable.

_Sam Newman_ writes in _Monolith to Microservices_ (O'Reilly, 2019):

> Fundamentally, in a system that consists of multiple independent services, there has to be some interaction between
> the participants. In a microservice architecture, _domain coupling_ is the result—the interactions between services
> model the interactions in our real domain.
We want to aim for domain-level coupling to avoid business logic drifting into current and future clients.

### Languages in "adopt"

HMPPS Digital is heading towards using Java/Kotlin and node.js as their primary language choices.

The [current HMPPS Digital tech radar][radar] elects the mentioned languages in _adopt_. It also mentions Ruby (for London);
however, the current direction is to reduce regional differences as it is difficult to hire developers who are comfortable
and happy working with multiple languages.

The department's talent pool reflects this split; there are:

- many node-focussed frontend specialists,
- many Java/Kotlin-focussed backend specialists,
- a few full-stack developers.

## Decision

We will **create standalone business interfaces (business/domain APIs) by default**.

We will **split the user interface** from the API's component to enforce the use of the business API by default
and better utilise the talent we have.

We realise this is an optimisation to build durable domain-coupled systems at the cost of some team autonomy.

## Consequences

**Benefits**

- The APIs we create can be used by other teams if they wish to hook into our business processes.
- There are reusable best practices already available from DPS for separate API components.
- The pool of talent who can join our team from HMPPS is larger.

**Challenges**

- We need to be more careful on what services we create and what API they have. This increases _governance_,
which we must keep lightweight.
- Contracts between the UI and the service components will take a while to stabilise, creating churn in the beginning.
- Expectations between the parts have to be clear to avoid blocking others or taking up time. [Consumer-driven contract testing][cdct] can mitigate.

In short, we gain _sustainability_ benefits by trading off _governance_ around the API contracts.

[radar]: https://ministryofjustice.github.io/hmpps-digital-tech-radar/docs/index.html
[cdct]: https://www.thoughtworks.com/radar/techniques/consumer-driven-contract-testing
[bounded-context]: https://martinfowler.com/bliki/BoundedContext.html
86 changes: 86 additions & 0 deletions doc/adr/0002-decouple-with-events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# 2. Decouple with events

Date: 2020-11-02

## Status

Accepted

## Context

The Dynamic Framework interventions are the first intervention type onboarded by this system.

### Many new teams

In the current probation programme landscape, we expect

- many new digital teams,
- many changes to existing systems,
- different times when integration happens as teams will have separate priorities.

Specifically, we will have other teams to

- assess risks and needs,
- manage a sentence,
- manage a supervision in the community,
- manage my progress.

All of these teams would have interest in actions carried out in interventions.

### Nature of probation

Additionally, probation

- reacts to what happened in real-life with the supervised service users,
- has various priorities, depending on what happened.

## Decision

The above qualities point towards a decentralised way of integration and notifying each other.

**We will use _domain_ events to decouple** from other production services.

Our domain event payloads will contain:

- transport-specific concerns, e.g. request and tracking IDs, publishing timestamps, etc.
- domain context, e.g. URL on how to find out more, intervention ID, service user ID, etc.
- core domain data, which is required to make sense of the event

The payloads will **not** contain further data by default. **Consumers can request further details via the business API**.

We do not intend to build event sourcing. We will **not** build event logs by default.

**Examples**

For example, our events may be:

- intervention completed
- service user appointment booked
- (many others)

## Consequences

We will build our intervention services to emit known significant domain events by default.

**Benefits**

- The decoupling allows us developing almost all user stories without waiting on endpoints to be finished.
- New and old systems can join as consumers any time.
- We contribute to an event _vocabulary_ for probation, enabling systems with similar responsibilities to
start publishing the same events.
- Same events from different systems enables us to start using the strangler fig pattern
by moving the source of those events elsewhere.

**Challenges**

- This architecture is asynchronous and distributed:
- Request tracking and error handling needs to be built-in to understand what happens in production.
- It is harder to set up a certain state for pre-production testing as we have to know what events have to happen.
- Lack of atomic transactions:
- We must not use events that need to be rolled back as a result of a consumer failing to process them.
- Creation, maintenance, and governance of the event contracts is difficult:
- We need to version the events from the beginning, to avoid backwards-incompatible changes blocking consumers.
- We need to use event names that are aligned and understood by everyone in HMPPS.
- We need to document which services emit which events to make discovery and testing easier.

In short, _governance_ will have a much more important role.
100 changes: 100 additions & 0 deletions doc/adr/0003-use-git-commits-to-explain-our-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# 3. Use Git commits to explain our work

Date: 2020-11-24

## Status

Accepted

## Context

We are going to be developing a new software system as a team.

### Understanding decisions

At any point in time, our codebase will be an aggregate of many influences. It
reflects, for example:

- conversations that we’ve had as a development team
- conversations with other members of the project team
- conversations with stakeholders
- technical facts we’ve learned from outside sources
- things we’ve learned from failed attempts
- ideas we’ve had as individual developers
- compromises that we’ve had to make in the interests of time

As the team develops the service, it is very likely that we’re going to have
questions about why the codebase is the way that it is. We might see a certain
piece of functionality that doesn’t make sense, or a piece of code that
seemingly could have been written more simply. In order to know whether we can
change these things, we need to first understand the context in which they were
originally done, so that we can assess whether the same conditions still hold.
Without being able to understand the motivation, we’ll find it difficult to
confidently make changes that affect these areas.

The answers to these questions might live in people’s memories. But people
forget things, so maybe not. Also, the development team is not static - today’s
developers might not be here tomorrow. This is especially true on this project,
where several developers come from external organisations and will leave the
project in the relatively near future.

We need a way to record these decisions so that they can be easily discovered by
the team at any point in the future.

Every change to the codebase is accompanied by a Git commit. The commit message
for this commit provides us with a space to explain any relevant context that
lives behind the change.

### Reviewing work

When we develop software in a team, we spend quite a lot of time reviewing other
developers’ code. Sometimes, multiple developers will review a single pull
request. We want to make sure that reviewing a pull request is not a
time-consuming process.

As developers opening a pull request, we should think about how we present our
work to others. We should make sure that we present our work in a way which is
easy for others to understand. We could do this by splitting our work into
logical Git commits, each of which make a single change, with an accompanying
commit message which explains the change and addresses any questions that we
think a reviewer might have about the change.

We should also consider how merge commits can impact the reviewability of our
pull requests. Merge commits stop the pull request from being a simple linear
sequence of commits. Also, the diff of a merge commit, as presented by GitHub,
can often be hard to understand.

### The Git repository is probably immortal

There are other places where some of the codebase’s influences might be recorded
— deliberately or not. For example, Trello cards and their comments, GitHub pull
request messages, GitHub pull request comments. There is sometimes a temptation
to think that these tools will be around forever, but this is not true. The Git
repository is the one of the few things that we can be pretty sure will be
around for the lifetime of the project.

## Decision

When we open a pull request, we will make sure that we present our work with a
Git commit history that tells a clear story of the work that we’ve done and why
we’ve done it that way.

Our pull requests will have a linear history, meaning that they will not contain
any merge commits.

The pull request message will be a summary of the work that we’ve done, but
any important information that it contains will also be in the commit messages.

If we make changes to a pull request, we will re-structure our commits to make
sure that they continue to tell a useful story.

When we merge a pull request into the main branch, we will preserve that pull
request’s commits, and not squash them into a single commit.

## Consequences

It takes time to produce good commits. It might take even more time if a
developer needs to become familiar with some Git functionality that they haven’t
used before, like rebasing. This might slow down an individual developer working
on a feature. But, even just the time saved by making code review easier for
multiple developers is likely to mitigate this.

0 comments on commit c2eac3b

Please sign in to comment.