Software Architecture

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

The great rules

These rules have been proven in projects and apply broadly.

Use SI-units within the system

When you have real-world values, use SI-units within the whole system.

Measure Name Unit
Length meter m
Mass kilogram kg
Time second s
Electric Current ampere A
Thermodynamic Temperature kelvin K
Amount of Substance mole mol
Luminous Intensity candela cd

When the environment (users, imported data, export formats) does require other units (e.g. if the user wants to input the data in inches or millimeters), do the conversion within a thin layer right at the boundary of the system.

The underestimated rules

This part contains rules that are often underestimated.

Possibilities increase complexity

Whenever there are multiple ways to achieve something, the overall complexity increases. Each possibility alone adds little complexity, but together they multiply. The complexities often do not add up (2+2+......+2+2=20), but multiply (2*2*......*2*2=1024).

Imagine a business process with 10 steps and each step has 2 variants. To test all variants of the business process, 1024 variants would have to be tested.

The rules of thumb

This part contains some rules of thumb that often apply.

Expect 2 to 10 times more effort

If you are not able to bear an increased effort of 2 to 10, you should not start the project.

Keep it small and simple

Software projects often grow significantly during development. If you do not start small and simple, the growth will exceed your project constraints.

When you need performance, design your algorithms accordingly

Choosing the right algorithm — O(n) instead of O(n²) — matters far more than optimizing individual steps.

When writing code, optimize for readability, not for performance

When you write code, optimize for readability, not performance. There will be very little code where you will have to sacrifice readability for performance.

Complex code is easier to write than to read

When you generate code that you think is hard to write due to its complexity, beware that you will have an even harder time reading it. When you write it, you are in a stream of thought where one leads to the other. When you return to the code a year later, you are not in the topic anymore and you may not have time to recreate the whole stream of thought again.

Linear code is simple code

When you can read a program sequentially, line by line, each line is a step, the code will be easy to understand. When you write code, think what it will look like when you step through the code with a debugger later. When you jump between files often, the code will not be easy to read.

The great questions

This part contains architectural questions that frequently come up.

Where to put the interface?

One frequent design decision is determining where to place interfaces in your architecture. There are three possibilities.

Independent Interface (I)

Independent Interface: Interface in separate project

Client-Owned (A)

Client-Owned Interface: Interface in high-level project

Provider-Owned (B)

Provider-Owned Interface: Interface in low-level project
Interface location Main benefits Main drawbacks Best fit
Interface project
  • Clean decoupling
  • Extra project
  • Interface provided by the architect, client and provider implemented in parallel by different teams.
  • Multiple clients and providers.
High Level project
  • Interface fits client perfectly.
  • Dependency inversion.
  • Simple unit testing.
  • Provider hard to reuse for other clients (provider depends on client abstractions).
  • Top down design (client-specific provider).
  • Multiple providers for one client (mock provider for unit testing).
  • Clean architecture.
Low Level project
  • Interface fits provider perfectly.
  • Client tightly coupled to provider.
  • One provider, many clients.
  • Fixed provider (e.g. interface to a hardware device).

If the client shall use a nice interface and the given provider interface does not really fit? Add an adapter class that implements the interface defined by the client and uses the interface defined by the provider.