Dependencies
Dependencies define how components relate to each other and how failures propagate through your system. They specify the logic that determines when a component is UP based on the state of its inputs.
In YAML, dependencies are declared per target component under the top-level dependencies map. Allowed keys: do, and, or, n_of_k.
Dependency Types
Direct (do:)
A direct (causal) dependency: the target is UP if its parents are UP. Use this for straightforward structural links.
Usage
- Works with any component type.
- When the target is a normal node, use
do:(don’t attachand/or/n_of_kto a normal target).
Example (direct deps)
root: web_app
components:
web_app: { type: logical }
web_tier: { type: normal, mttf: 20000, mttr: 2 }
api_tier: { type: normal, mttf: 25000, mttr: 2 }
db_tier: { type: normal, mttf: 30000, mttr: 1 }
dependencies:
web_app:
do: [web_tier, api_tier, db_tier]
AND (and:)
Target is UP only when all listed inputs are UP (serial/critical-path behavior). Model this on a logical node.
Use for
- Serial systems where every element must work
Rules
- The target must be a logical component
Example (AND gate)
root: app_core
components:
app_core: { type: logical }
lb: { type: normal, mttf: 40000, mttr: 1 }
db: { type: normal, mttf: 30000, mttr: 2 }
dependencies:
app_core:
and: [lb, db]
OR (or:)
Target is UP when any listed input is UP (redundancy/alternate paths). Model this on a logical node.
Use for
- Redundant services and instant failover
- Alternative service paths
Rules
- The target must be a logical component
Example (OR gate)
root: api_virtual
components:
api_virtual: { type: logical }
api_a: { type: normal, mttf: 18000, mttr: 1 }
api_b: { type: normal, mttf: 18000, mttr: 1 }
dependencies:
api_virtual:
or: [api_a, api_b]
Use
or:for instantaneous redundancy. If failover success depends on time to react, model that timing with a latch and wire it usingdo:(see patterns below).
N-of-K (n_of_k:)
Target is UP when at least n of k inputs are UP (quorum/partial redundancy). Model this on a logical node.
Use for
- Quorum services, partial redundancy
- Load sharing with a minimum capacity requirement
Rules
nmust satisfy1 ≤ n ≤ len(inputs)- The target must be a logical component
Example (quorum 2-of-3)
root: storage_quorum
components:
storage_quorum: { type: logical }
disk1: { type: normal, mttf: 50000, mttr: 4 }
disk2: { type: normal, mttf: 50000, mttr: 4 }
disk3: { type: normal, mttf: 50000, mttr: 4 }
dependencies:
storage_quorum:
n_of_k:
n: 2
inputs: [disk1, disk2, disk3]
Dependency Structure (schema shape)
do: array of component namesand: array of component namesor: array of component namesn_of_k: object withn(integer) andinputs(array of names)
All are declared under dependencies:<target>.
Core Rules (validator)
Configuration rules
- One dependency type per target. Don’t mix
do,and,or,n_of_kon the same target. Use hierarchical logical nodes instead. - Targets exist. Every dependency name must reference a component defined under
components. - Quorum bounds. For
n_of_k, ensurenis between1and the number ofinputs. - No cycles. Avoid circular dependencies.
- Type discipline. Attach
and/or/n_of_kto logical targets; usedofor normal targets.
Modeling Patterns
Hierarchical Composition (recommended)
Build complex logic by layering logical nodes so each node uses one dependency type.
Example (hybrid AND + OR)
root: web_app
components:
# logical aggregators
web_app: { type: logical }
app_core: { type: logical }
api_virtual: { type: logical }
# normals
lb: { type: normal, mttf: 40000, mttr: 1 }
db: { type: normal, mttf: 30000, mttr: 2 }
api_a: { type: normal, mttf: 18000, mttr: 1 }
api_b: { type: normal, mttf: 18000, mttr: 1 }
dependencies:
app_core:
and: [lb, db] # BOTH required
api_virtual:
or: [api_a, api_b] # EITHER API works
web_app:
and: [app_core, api_virtual] # combine tiers
Time-Constrained Failover (with latches)
Use latches to model success-within-deadline behavior; connect them with do:. The edge expresses coupling; the latch is activated on parent failure by the engine’s latch semantics.
Example (DB failover with deadline)
root: service
components:
# logical aggregators
service: { type: logical }
failover_path: { type: logical }
db_virtual: { type: logical }
# primaries/standby + latch
db_primary: { type: normal, mttf: 24000, mttr: 1 }
db_standby: { type: normal, mttf: 24000, mttr: 1 }
db_failover: { type: latch, mttf: 0.5, max_delay: 1.0 } # 30 min avg, 1 h deadline
dependencies:
# Latch listens to primary’s failure (engine triggers on failure even though the edge is `do`)
db_failover:
do: [db_primary]
# Failover path requires both: successful failover AND standby availability
failover_path:
and: [db_failover, db_standby]
# Virtual DB is available if primary is up OR the failover path is up
db_virtual:
or: [db_primary, failover_path]
# Service depends on the virtual DB (extend with app/web tiers as needed)
service:
do: [db_virtual]
Best Practices
Do - Model structure with logical nodes; keep normal nodes simple and causal. - Start from high-level dependencies, refine iteratively. - Use consistent, descriptive names for clarity in diagrams.
Don’t
- Mix multiple dependency types on the same target (breaks validation).
- Leave dangling references (every dependency name must exist under components).
Next Steps
- → Back to Components
- → Continue with Model management