Solution architecture is all about deriving architectural direction and vision to the organisation, translating areas of development or domains into well-organised self-directed teams, providing guidance on how to develop cross-functionally.
Note from Author
Organisation
If the organisation has many departments or teams that develop software, or is part of the process in developing it, it is important that these organize themselves and the processes they follow in an efficient manner.
- Stakeholders
- Systems Design
- Requirement Management
- Project Management
Common Workflow & Conventions
Identifying commonalities across departments and teams, and streamlining the workflow and conventions employed allow for efficient cross-domain work and alleviate double-work. If are different conventions or indeed none or few, establishing common conventions and ways of working is paramount to ensure a smooth and fast development flow.
Note from Author
Guidelines
- Style guide — use an existing one, don't invent your own. Make amendments to the existing one if absolutely necessary.
Code Management
Note from Author
In order to illustrate how to manage your code we will have to use a specific language for the examples, but the concepts will carry over to any project. The core of the concept is that of modularity. For the examples I will be using C++.
File StructuredocARCHITECTURE.md High-level description of the architecturemodules/module_0 Self-contained unit of the systemdoc Documentation of the module (UML diagrams, markdown, etc.)inc Public API for the modulesrc Implementation of the modulemock Mocks of the module, for use in dependent module's teststest Contain tests of this module's functionalityunit Unit-tests that mock any of this module's dependenciestarget Target tests that tests the behaviour of the module deployed onto a targetREADME.md A high-level description of the module and how to use itmodule_1module_2lib Contains external dependencies - usually weak references like git submodulessdk vX.Y.Z External libraries like an SDKpytest vX.Y.Z Frameworks for target-testing (PyTest is just an example)googletest vX.Y.Z Frameworks for unit-testing (Google Test is just an example)scripts A collection of scripts useful to the developercmake CMake-specific scriptsmodule.cmaketools Contains tools used in the projectCHANGELOG.md Used to track changes across releases and generate release notes.README.md Used to give guidance on how to build, deploy and release.
Pipeline Management
Note from Author
Branch & Release Strategy
Note from Author
Semantic Versioning
Any and all projects should have a well-defined way of signifying release versions. For this I recommend following the format specified by Semantic Versioning.
Change Log
It is important to establish a well-defined way of tracking changes in projects. For this I recommend following the guidance of Keep A Changelog.
Conventional Commits
Note from Author
Read about it in detail at Conventional Commits
Semantic Releases
Bringing all of these together to form a fully automated solution for version management, semantic release alleviates a lot of manual work and streamlines the process.
Note from Author
Read about it in detail at Semantic Release
Identify Business Needs
Note from Author
- As a business I want to ...
Identify Domains
The business needs map into domains which the organisation will distribute work between.
A domain is to provide the functionality of the domain and only that. As such, for software-related domains, we will consider the product of the domain to deliver a platform. More specifically, a software platform.
Dependency Inversion
Note from Author
The principle is expressed in two statements
A: High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces).
B: Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
Read more about it at Dependency Inversion
Target Integration
A target integration is in this context refers to the act of integrating a domain into a particular target. A target is also a rather abstract term, but appropriately so as it may be anything that makes the domain functionality available on the target which runs it. This could be a specific OS, like Windows or MacOS, or mobile targets like Android and iOS. Or, more exotically, it could be specific hardware integrations for embedded systems.
Put in another way, an integration is merely adapting the domain to a specific system we want to use it on. And there's a keyword ‐ use. We therefore have the relationship shown below. More specifically, since we want these development areas to be able to work independently of each other the integration shall use a specific version of the domain.
Loading diagram...
This indirection on the integration means we can implement a domain toward a specific target. That is, we can use the domains on different target platforms.
Domain Repository/my_domain/modules/filter abstract filter - provides the interface for integration/butterworth_filter butterworth filter - uses the filter interface/fast_fourier_transform
The domain supplies domain-level features. That is, features that solely are defined and implemented by the domain itself ‐ not project-level features. Consider for instance the domain of signal processing, which might provide features like filters or algorithms.
Integration Repositorymy_integration/modules implementations for the modules of the domain/filter implementation of the abstract filter provided by the domain/lib/my_domainv1.2.3 git submodule pointing to v1.2.3 of the domain/modules
Projects & Variants
A variant is a concrete build of project.
Loading diagram...
This gives us the following folder structure.
Project Repositorymy_project/apps Contains variants we would like to build for this project/variant_a Could be the consumer variant/variant_b Could be a test-specific variant/variant_c Could be a custom build for different department/modules/lib/my_integrationv1.2.3 git submodule pointing to version 2.6.1 of the integration/modules/lib/my_domainv1.2.3 git submodule pointing to version 3.2.1 of the domain
With these two levels of indirection we have a well-defined relationship between domains, their integrations into target-specific implementations, as well as a project-specific configurations.
Composable Architecture
Of course, different products make use of multiple domain-level features in order to provide a user-level feature. Hence, a product repository will usually make use of multiple integrations of domains.
Loading diagram...
Project Repositorymy_project/modules/lib/my_integration_av2.6.1 git submodule pointing to version 2.6.1 of the integration/modules/lib/my_domain_av3.3.4 git submodule pointing to version 3.3.4 of the domain/my_integration_bv1.2.6 git submodule pointing to version 1.2.6 of the integration/modules/lib/my_domain_bv2.1.0 git submodule pointing to version 2.1.0 of the domain/my_integration_cv3.1.8 git submodule pointing to version 3.1.8 of the integration/modules/lib/my_domain_cv1.1.4 git submodule pointing to version 1.1.4 of the domain
Building Bridges
Now a natural question arises. How do we align interfacing between different domains?
From our requirements, we have well-defined features on a system-level, which are refined into subsystem requirements. These requirements map directly into the interfaces we will need to provide within the respective domains. These should be formulalized in the form of a bridge pattern1.
Note from Author
Summary
- Establish a healthy developer community and environment
- Streamline your workflow and processes across departments and teams.
- Map business needs to development domains and organise architecture around these domains.
- Provides input to management for organisational structure.
- Lays the foundation for cross-domain development and cross-department collaboration.