Interfaces & Protocols (interfaces.py, protocols.py)¶
foundation separates nominal contracts (interfaces.py —
abc.ABC classes requiring explicit inheritance) from structural
contracts (protocols.py — typing.Protocol classes usable via duck
typing). Every foundation class implements the nominal interfaces
listed in its own spec; the structural Protocols exist so external
code (domain packages, third-party plugins) can conform without
inheriting from foundation base classes at all — important for
future non-Python ports where "interface" maps to a trait/typeclass
rather than an abstract base class.
interfaces.py¶
Identifiable¶
- Responsibility: Guarantee every implementer exposes a stable identity usable for equality, hashing, and lookup.
- Contract:
id: EntityId(property)uuid: UUID(property)__eq__(other) -> bool— identity-based equality__hash__() -> int— identity-based hash- Implemented by:
Entity,System,Relationship,Constraint,Event,Observation,Knowledge,Interaction.
Observable¶
- Responsibility: Guarantee an implementer can produce an
Observationof its current condition without mutating it, and can be subscribed to for its own lifecycle Events. - Contract:
observe(observer: str | None = None) -> Observationsubscribe(event_type: EventType, handler: EventHandler) -> SubscriptionId- Implemented by:
Entity,System,Attribute,Interaction.
Serializable¶
- Responsibility: Guarantee an implementer can round-trip to and from every supported wire format.
- Contract:
serialize(format: SerializationFormat = SerializationFormat.JSON) -> bytes | strdeserialize(data: bytes | str, format: SerializationFormat = SerializationFormat.JSON) -> Self(classmethod)- Implemented by: every foundation class except pure-behavioral
helpers (
Behaviour's hooks are the sole excluded piece, per its own spec).
Cloneable¶
- Responsibility: Guarantee a safe, deep, Identity-refreshing copy operation.
- Contract:
clone(**overrides: Any) -> Self- Implemented by:
Entity,System. - Not implemented by
State,Interaction,Observation,Knowledge,Event: these are immutable historical facts; "cloning" one would misrepresent provenance. New instances are created via their owncreate()factory instead.
Validatable¶
- Responsibility: Guarantee an implementer can self-report correctness against its declared rules.
- Contract:
validate() -> ValidationResult- Implemented by:
Entity,System,Property,Attribute. - Note:
StateandConstraintexpose validation-shaped methods (State.validate(constraints),Constraint.evaluate(state)) with a different signature (they need an external argument), so they do not implement this zero-argument interface directly — documented explicitly to avoid an implementer assuming otherwise.
Comparable¶
- Responsibility: Guarantee structural/value comparison beyond identity equality.
- Contract:
compare(other: Self) -> ComparisonResult__lt__,__le__,__gt__,__ge__where a total order is meaningful (optional — declared per-class; e.g.TimeandIdentitysupport ordering,Entitysupports only equality-shaped comparison by default).- Implemented by: all foundation classes.
Timestamped¶
- Responsibility: Guarantee an implementer exposes creation/update
instants using the shared
Timetype. - Contract:
created_at: Time(property)updated_at: Time(property, may equalcreated_atfor immutable value objects)- Implemented by: all foundation classes.
protocols.py¶
Structural mirrors of the above (usable without inheritance), plus callback-shaped Protocols not modeled as classes:
EventHandler (Protocol)¶
__call__(event: Event) -> None
EventBus.subscribe, Behaviour.pre_hooks/post_hooks.
Validator (Protocol)¶
validate(value: Any) -> ValidationResult
Property, Attribute, validators.py.
Transformer (Protocol)¶
apply(state: State) -> State
Transformation.apply for cases where a domain
package wants to supply a bare function instead of a full
Transformation subclass.
IdentifiableP, ObservableP, SerializableP, ComparableP,¶
TimestampedP — structural (Protocol) mirrors of every nominal
interface above, named with a P suffix to avoid clashing with the
ABC names, for callers that want isinstance()-style structural
checks without requiring inheritance from interfaces.py.
Why both nominal and structural versions exist¶
- Nominal (
interfaces.py) is the primary contract everyfoundationclass inherits from — it gives shared default behavior hooks (e.g.Comparable.__lt__default implementation delegating tocompare()) and a single place to document the contract. - Structural (
protocols.py) exists for interoperability: a third-party plugin class that cannot or should not inherit fromfoundationABCs (e.g. it already inherits from something else) can still be accepted anywherefoundationcode type-hints against a Protocol, and can still passisinstance()checks against Protocols decorated@runtime_checkable. - This mirrors the split that will be needed in the multi-language ports anyway: nominal interfaces map to C++ abstract classes / Java interfaces / Rust traits with default methods; structural Protocols map to Go interfaces / Rust trait bounds without inheritance / Julia duck-typed multiple dispatch.