Software Architecture

Ruǎnjiàn Jiàgòu 软件架构 (DISA, DABM)

The Idea

You can understand a simple thing by just observing it from the outside.

If a thing is not simple, you won't be able to understand it by observing it just from the outside. To understand it, you split it into smaller things that you can understand by just observing from the outside. Usually, you can't really split the thing: the parts will still interact with one another. A good decomposition will lead to small and simple interactions between the parts.

One simple thing

One simple thing

Three parts

A good architecture makes it easy to find the effects of an action. The less dependencies you have between the components, the easier it is to follow the path from the action to the effects. So, a good architecture decomposes a system in parts that minimizes dependencies between the parts.

Complex

When the system is decomposed into only a few (e.g. 3) parts, a system architecture where every part depends on every other part may not seem unnecessarily complex.

Three parts with all bidirectional dependencies

Simple

When a system is decomposed into only a few (e.g. 3) parts, a system architecture where the parts depend only on one other part looks a bit simpler, but may not seem worth the effort. If you start like this it is important to realize that adding more parts will lead to a mess.

Three parts with simple linear dependencies

Nine parts

Complex

Nine parts all bidirectionally connected

Simple

Nine parts in three layers with simple dependencies

What is a good decomposition?

A good decomposition is generally one that makes it easier to understand the system.

Approach A - Minimize the number of connections per module

With this approach, we calculate the ratio between the connections and the possible connections. You get a number between 0 and 1 with lower values indicating a better decomposition. A value of 0 is the perfect decomposition, 1 is the worst decomposition possible (using this metric). One part of the work of an architect is to minimize this value.

$$\text{Quality}_{\text{Decomposition}} = \frac{\text{Number of connections}}{(\text{Number of modules}) \times (\text{Number of modules} - 1)}$$

As an example, let's have a look at a decomposition of a system in the components A, B and C. This leads to 3 * 2 = 6 possible connections (AB, AC, BA, BC, CA, CB). A bidirectional connection A ↔ B counts as two connections (A → B and B → A).

If your decomposition has 0 connections, its quality score is 0 — the best possible value. This will be the case if you can decompose the system into modules that are completely independent of one another.

If they are not independent, 2 connections will be as good as possible, the quality of your decomposition is $\frac{2}{3 \times 2} = \frac{1}{3}$.

If you have 6 connections (all three components are bidirectionally connected), the quality of your decomposition is $\frac{6}{3 \times 2} = 1$, which is the worst value possible.

Approach B - Change in Environment to Change in System relationship

List possible environment changes with their probabilities. Then calculate what percentage of the system each change affects. Do this for several decompositions. The smaller the percentage, the better the decomposition.

Change in Environment (Probability) System affected (0=nothing, 1=all) Total
Inform via letter instead of email (0.1) 1 0.1 * 1 = 0.1
Prices calculated daily automatically (0.05) 1 0.05 * 1 = 0.05
Not only one but multiple sites (0.5) 1 0.5 * 1 = 0.5
Responsible can define a delegate (0.8) 1 0.8 * 1 = 0.8
Total 0.1 + 0.05 + 0.5 + 0.8 = 1.45
Part A (0.2 of the system) affected Part B (0.4 of the system) affected Part C (0.4 of the system) affected Total
Inform via letter instead of email (0.1) 0.2 0 0 0.1 * 0.2 = 0.02
Prices calculated daily automatically (0.05) 0 0.4 0 0.05 * 0.4 = 0.02
Not only one but multiple sites (0.5) 0 0 0.4 0.5 * 0.4 = 0.2
Responsible can define a delegate (0.8) 0.2 0 0.4 0.8 * (0.2 + 0.4) = 0.48
Total 0.02 + 0.02 + 0.2 + 0.48 = 0.72

Some simple decompositions

Layering

User Interface Layer
Business Logic Layer
Data Access Layer

Tiering

The idea here is, that you run every layer on a separate computer. The presentation tier runs on the users computer (e.g. in a browser or terminal). The business logic in the backend (e.g. a web server) and the data tier on a database server.

Presentation Tier
Application Tier
Data Tier

Feature Folders

The idea here is, that you decompose by feature first (so, a feature folder containing all layers/tiers necessary for the feature).

Feature 1
User Interface
Business Logic
Data Access
Feature 2
User Interface
Business Logic
Data Access
Feature 3
User Interface
Business Logic
Data Access

Structural elements for decomposition in C

Project
Directory
Source File
Function
Statement

Structural elements for decomposition in C#

Solution
Project
Namespace (~path)
Class (~file)
Method
Statement