Architectural Design
Overview and Hierarchy
Software in rules_score is structured in three levels:
dependable_element (SEooC — complete Safety Element out of Context)
└── component (groups units; owns component-level integration tests and requirements)
├── unit (smallest independently verifiable architectural element: implementation + unit tests)
└── component (components can be nested for deeper hierarchies)
└── unit
Two rules apply:
unittargets must always be wrapped in acomponent— they cannot be placed directly underdependable_element.componenttargets can be nested: a component may contain other components as well as units, allowing arbitrary depth.
This hierarchy exists for two complementary reasons:
Interrelations between units and components — A component defines a clear boundary within which its units collaborate. Grouping units into components keeps inter-unit coupling explicit and local, while the component’s public interface controls what the rest of the system can depend on. The static architecture diagrams document exactly which components expose which interfaces, preventing accidental cross-boundary dependencies.
Interface-driven safety analysis — The
public_apidiagrams at the SEooC level define the interfaces that external consumers may call. Failure modes in the FMEA reference individual interface items by name, establishing a direct traceability link from the safety analysis back to the architecture. Without a well-defined interface boundary this traceability would be impossible.
Each level of the hierarchy has a corresponding design artifact:
Level |
Design rule |
Content |
|---|---|---|
SEooC / component |
|
Static structure, dynamic behaviour, public API |
Unit |
|
Implementation Details, unit-level sequences |
A consistency check at build time verifies that every component and unit in the Bazel implementation tree also appears in the PlantUML target architecture diagrams. If anything is out of sync the build fails with a descriptive error.
Static Architecture
The static view describes the structural organisation of your software: what components and units exist, how they relate to each other, and which dependencies they carry. It is the primary input for the architecture consistency check.
PlantUML
Write a PlantUML class or component diagram that names every component and unit from your Bazel BUILD file.
@startuml MySeooc_StaticDesign
package "MySeooc" as MySeooc <<SEooC>> {
component "KvsComponent" as KvsComponent <<component>> {
component "KeyValueStore" as KeyValueStore <<unit>>
component "StorageBackend" as StorageBackend <<unit>>
}
}
@enduml
Valid PlantUML Definitions
The validator identifies elements by their stereotype, not by the PlantUML keyword used. Both package and component keywords are accepted at each level.
Stereotype |
Valid PlantUML keywords |
Meaning |
Bazel rule |
|---|---|---|---|
|
|
Safety Element out of Context boundary |
|
|
|
Architectural component |
|
|
|
Leaf implementation unit |
|
Ports and Interface Bindings
Elements with stereotype <<SEooC>> or <<component>> may declare ports and bind them to interfaces. This documents which external interfaces the element requires or provides.
@startuml MySeooc_StaticDesign
package "MySeooc" as MySeooc <<SEooC>> {
component "KvsComponent" as KvsComponent <<component>> {
component "KeyValueStore" as KeyValueStore <<unit>>
}
portin " " as p_storage ' required interface port
portout " " as p_api ' provided interface port
}
interface "score::storage" as storage
interface "kvsapi" as kvsapi
p_storage -( storage : requires
p_api )- kvsapi : provides
@enduml
Rules:
portin/portoutare declared inside the<<SEooC>>or<<component>>element.-(binds a required (incoming) interface;)-binds a provided (outgoing) interface.The
--()lollipop syntax (e.g.port --() Interface) is treated as a plain association and does not carry interface-binding semantics.Plain
packagewithout a stereotype cannot carry interface bindings.
Bazel
The PlantUML diagrams capture intended structure; the Bazel rules model the actual implementation. Using the same example as the diagram above — SEooC MySeooc containing component KvsComponent with units KeyValueStore and StorageBackend — the three rules work together like this:
architectural_design
declares which diagram files belong to which view category:
load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", "architectural_design")
architectural_design(
name = "my_arch",
static = ["static_design.puml"], # the MySeooc_StaticDesign diagram above
dynamic = ["sequence_design.puml"],
)
unit
one target per leaf unit (<<unit>> stereotype) in the diagram. The unit name must match the name used in the PlantUML. It ties together implementation targets, test targets, and an optional unit_design target (see Software Unit Design):
load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", "unit")
# Unit for KeyValueStore
cc_library(name = "kvs_lib", srcs = ["kvs.cpp"], hdrs = ["kvs.h"])
cc_test (name = "kvs_unit_test", srcs = ["kvs_test.cpp"], deps = [":kvs_lib"])
unit(
name = "KeyValueStore",
unit_design = [":kvs_unit_design"],
implementation = [":kvs_lib"],
tests = [":kvs_unit_test"],
)
# Unit for StorageBackend
cc_library(name = "storage_lib", srcs = ["storage_backend.cpp"], hdrs = ["storage_backend.h"])
cc_test (name = "storage_unit_test", srcs = ["storage_test.cpp"], deps = [":storage_lib"])
unit(
name = "StorageBackend",
unit_design = [":storage_unit_design"],
implementation = [":storage_lib"],
tests = [":storage_unit_test"],
)
component
groups the units that belong to KvsComponent in the diagram. It aggregates one or more unit (or nested component) targets and links them to component-level requirements. Integration tests that verify the units working together are declared here:
load("@score_tooling//bazel/rules/rules_score:rules_score.bzl",
"component", "component_requirements")
component_requirements(
name = "kvs_comp_req",
srcs = ["component_requirements.trlc"],
deps = [":feature_req"],
)
# The component maps to KvsComponent in the PlantUML diagram
component(
name = "KvsComponent",
requirements = [":kvs_comp_req"],
components = [":KeyValueStore", ":StorageBackend"],
tests = [],
)
Dynamic Architecture
The dynamic view describes behavioural aspects — sequences of interactions, state transitions, and activity flows. Dynamic diagrams document how your software behaves at runtime. They are not validated against the Bazel structure at build time.
PlantUML
@startuml MySeooc_WriteSequence
actor Caller
participant KeyValueStore
participant StorageBackend
Caller -> KeyValueStore : write(key, value)
KeyValueStore -> StorageBackend : flush()
StorageBackend --> KeyValueStore : OK
KeyValueStore --> Caller : Result::Ok
@enduml
Bazel
architectural_design(
name = "my_arch",
static = ["static_design.puml"],
dynamic = ["sequence.puml"],
)
Public API
The public API view describes the interface your SEooC exposes to its environment. These diagrams are linked to safety analysis: FailureMode records reference interface items by name (via the interface field), enabling traceability from each failure mode back to the architecture.
PlantUML
@startuml MySeooc_PublicApi
interface "KeyValueStore" as KVS {
+ write(key: string, value: bytes): Result
+ read(key: string): Optional<bytes>
}
@enduml
Bazel
architectural_design(
name = "my_arch",
public_api = ["public_api.puml"],
)
The public_api attribute also generates traceability items that can be referenced by fmea targets (see Dependability Analysis) via the arch_design attribute.
RST and Markdown Wrappers
When you want to combine a diagram with text, create an RST or Markdown file that embeds the diagram using the .. uml:: directive (RST) or the MyST equivalent.
RST wrapper example:
Static Architecture
-------------------
The following diagram shows the component structure of MySeooc.
.. uml:: MySeooc_StaticDesign.puml
Include both the wrapper file and the referenced .puml file in the same Bazel list — the build needs both:
architectural_design(
name = "my_arch",
static = [
"static_design.rst", # wrapper with prose
"MySeooc_StaticDesign.puml", # diagram referenced by the wrapper
],
)
Rule Reference: architectural_design
For the complete architectural_design attribute reference, see architectural_design in the rule index.