Processes of Decomposition

Posted on November 22nd, 2021

Part of Notes on Software Design

The design of a system outlines what the system is and is not for, describes the overall architecture following which the system will be developed, and details its static structure and dynamic behaviour.

At this point, we have some amount of user profiles/personas, scenarios, storyboards, wireframes, and (non-)functional requirements. We can now startdecomposing the system concept into a theoretical representation.

Documenting the design should be done various levels of complexity/abstraction. Without simplification, the design process would be trivial since it would match the implementation identically.

Functional design concentrates on the functionality of the system, whereas operational Design concentrates on the operational aspects; e.g., hardware set-up, what functionality runs where, network design. With the movement in modern software development towards the cloud, this is becoming less relevant.

Good design addresses and includes extensibility, modularity, reusability, maintainability, usability, and clarity.

Balance for all involved

When designing an application, a requirement will have many possible design solutions; the users, developers, and maintainers must all be considered, as an imbalance will lead to detriment for everyone.

For example, over-simplified applications will be easier to create but the end-user will not be happy with the system

By contrast, complex systems will be harder to develop and maintain; this costs time and money which ultimately affects the end-user.

Using patterns

Most 'new' solutions are simply an adaptation of existing applications, so the wheel need not be reinvented. More importantly, use design patterns which are relevant to the solution. e.g., N-tier or lter for a .NET API written with C#.

Design patterns also accommodate for changes and maintenance, as new designers will have a fundamental understanding of the system before tacking domain knowledge, implementation, etc.

Representation

Designs should be represented with models because they're a proven and well-accepted method.

They should demonstrate the functionality of the system at different levels of complexity/abstraction. This makes the design more accessible, as the system can be understood at the level desired by an individual. Inherently, it is important to ensure a representation is not oversimplified or important details may be masked.

Provided the developers of the system respect the design, a system's model will closely resemble the technical implementation of the system, so getting the models correct is key to smooth development.

Making Decisions

Although some aspects of the solution will have a clearly optimal design, most aspects of a project will require some level of decision-making.

As such, it is important to document any/all decisions. Not only does it simply provide evidence of the decisions made by the team, but producing a standardized document ensures all factors motivating the decision are valid.

Decision[ID] - [Name]
Subject AreaArea of concern
TopicTopic of interest
SummarySummary of the decision made, ideally in the imperative tense
Problem StatementA short description of the problem, i.e., what is being decided
AssumptionsWhat is believed to be true about the context of the problem
MotivationWhy is the decision important?
AlternativesA list of alternatives and explanations
DecisionThe decision taken, possibly with references to related work
JustificationWhy the decision was made
ImplicationsWhat impact the decision will have
JustificationA list of requirements that are generated by this problem
ImplicationsA list of related decisions
Example Decision Template

System Boundary

(This is not well explained; most of this is my interpretation so it might be wrong 🤷‍♂️)

A system boundary determines where aspects of the system are sourced by decomposing the top-level functionality of the solution; basically:

Think of it as an extension of scope; after defining what the system should do, the system boundary determines whether each aspect of the system is 'in scope' for creation or resourced from an existing solution.

System boundary also considers human sources for input/output, so these devices should be considered as well, e.g., PIN number entry pad.

Lastly, system environment refers to anything which affects/can be affected by the system, excluding the system itself.

Representation

A system context diagramis the typical way to represent the system boundary, represented as a simple, 'free form' sketch. The relationships between systems can be ambiguous and considers:

System Context Diagram

Solution Structure

Following the system boundary, the solution structureidentifies the main conceptual elements (subsystems) of the system and their relationships; e.g., the core subsystem, connections, data stores, users, external systems, etc.

Think about:

Representation

An architecture overview diagramis the typical way to represent solution structure. It is a (usually) static, informal diagram with supporting text.

The definition is pretty loose, so they can identify a wide range of subsystems. However, there are some typical organisational groupings: users help to identify the roles of people using the system; channels identify how the system can be used/accessed, e.g., Browser, Mobile, Integrated PoS terminal; application identifies the subsystems; and resources contain the systems used by the application. Additionally, splitting the groups further or color-coding can add more detail.

It does not need to be implementation-specific, i.e., 'Database' could be sufficient, but 'AWS Cloud Hosting' is also acceptable.

Architecture Overview Diagram

Refinement

Following the solution structure, the application's subsystems are iteratively (and recursively) designed in the refinement process:

Types of Subsystems

The definition of a subsystem is very flexible but it basically has to be sizeable. For example, look at the subsystems for a remote control:

  1. Case
  2. Power supply
  3. Circuit board
    1. Chips
    2. Fuses
    3. IR blaster
    4. Integrated circuits
      1. Capacitors
      2. Resistors
      3. Bridges

There are however some very common subsystem types.

ComponentA modular unit of functionality, usually identified the architecture definition.

Its state and operations are declared using one or more interfaces, so the definition should not be implementation specific.

They are functionally independent, meaning they do one thing.

Components are defined at a technical level, e.g., MessageQueue, or application level, e.g., OrderProcessor.

Class The usual.

Entity A class with a conceptualisable existence, most often physical like a Person, or not, like a Report (value objects say huh?).

ServiceA group of specifications, exposed through JSON, API, interfaces, etc.

They can be consumed, e.g., StockPriceService, UserAuthoriser, or provided using components/classes.

It's all about the process

Generally, this process considers the structure of each subsystem, and how they interact.

Start with identification of the subsystems, and assign responsibility, based on the requirements document. Define the relationships between subsystems and outline their interactions. Use existing design/architecture patterns to ensure the system interactions are clean and consistent.

Next comes specification, which tackles implementation detail by creating the interfaces for each subsystem. This includes their operations, i.e., parameters, return values; and their contract behaviour, such as pre- and post-conditions.

Guidelines

Cohesion The strength of dependency within the system. The goal is to create functionally independent but interdependent subsystems, meaning each subsystem has one responsibility and utilises other subsystems to delegate other responsibilities required to function.

❌ Low cohesion means a subsystem's functionalities have no meaningful relationship, only in time, i.e., a process may use them in the same block of code.

✔️ High cohesion means the opposite. For example, the functionalities can use each other (functional), use the output of one as an input for another (sequential), or compose a greater procedure to be used in order (procedural).

Coupling The strength of dependencies between subsystems. Good coupling allows dependencies to change their implementation without requiring changes to its dependants.

❌ Strong coupling means subsystems refer to the same global data 'area' and the internal logic of the subsystem cannot be isolated from the implementation of its subsystems.

✔️ Loose coupling the logic between subsystems to be isolated, and communication is handled with fundamental data types like domain entities or technical classes.

Isolation The degree to which the product depends on technologies. It also considers recognisability of its design and implementation.

❌ Low isolation means subsystems depend on technologies, such as a native app; and fail to use standard patterns, e.g., directing with a database directly.

✔️ High isolation means the opposite, e.g., a web app (any platform) which uses an ORM to omit database specifics from the application code.

Layering Partitioning the concerns/responsibilities of a large subsystem into general categories.

🤷‍♂️ Horizontal layering categorises by functional responsibility, e.g., presentation, business, and data layer. This is referred to N-tier architecture.

🤷‍♂️ Vertical layering categorises by business/domain responsibility, e.g., a query, handler, and view model toCreateProduct. One example is onion architecture.

Types of Models

Static models A static, logical view of the application including state and relationships (e.g., entity relationship, class, component, service model).

Dynamic models (or behavioural models) demonstrate the interactions between the subsystems (e.g., use-case diagram, sequence diagrams).

End-user interactions are modelled with use-case diagrams.

Technical interactions within a subsystem show the complex behaviours of the implementation like method and service calls, modelled with flowcharts or activity diagrams.

Modelling between systems is captured with interactions models: when ordered by time, a sequence diagram; when ordered by organization, collaboration or communication diagrams.