Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
75f44d2
refactor: restructure Message into message/ subpackage with abstract …
gbrennon Jul 23, 2026
0a35736
refactor: replace Serializable with MessageCodec/DictMessageCodec in …
gbrennon Jul 23, 2026
f86f374
feat: add reconstitute classmethod to AggregateRoot
gbrennon Jul 23, 2026
c706994
refactor: rename _from_payload_fields to from_payload_fields across t…
gbrennon Jul 23, 2026
270abc3
test: reorganize message tests into decorators, metadata, and seriali…
gbrennon Jul 23, 2026
d783188
docs: update reference docs for message and serialization API changes
gbrennon Jul 23, 2026
47e8c91
docs: document ABC interaction in message_dataclass decorator
gbrennon Jul 23, 2026
c15d682
style(docs): use plain prose for ABC interaction paragraph in message…
gbrennon Jul 23, 2026
972a202
fix(infrastructure): tighten DictMessageCodec type constraint and fix…
gbrennon Jul 23, 2026
661a9f6
docs(foundation): fix MessageMetadata class docstring and _payload docs
gbrennon Jul 23, 2026
8cc3196
fix(infrastructure): remove unnecessary cast after type constraint ti…
gbrennon Jul 23, 2026
cc745cf
refactor(infrastructure): promote encode/decode to public abstract me…
gbrennon Jul 23, 2026
daa3411
refactor(infrastructure): use ABC base class instead of FinalABCMeta …
gbrennon Jul 23, 2026
b119b37
fix(infrastructure): cache reconstituted aggregates after event replay
gbrennon Jul 23, 2026
bd35367
fix(foundation): remove duplicate Args block and dead frozen param fr…
gbrennon Jul 23, 2026
13194df
test(foundation): add regression tests for message equality and hashing
gbrennon Jul 23, 2026
74ac212
docs(foundation): clarify that MessageMetadata.created_at preserves i…
gbrennon Jul 23, 2026
74cb315
fix(infrastructure): remove collect_events call from AggregateReposit…
gbrennon Jul 23, 2026
af4be28
fix(infrastructure): raise descriptive errors for missing metadata ke…
gbrennon Jul 23, 2026
43e5882
fix(foundation): guard against unknown fields in from_payload_fields
gbrennon Jul 23, 2026
70b6f3c
fix(foundation): replace misleading or-pattern with explicit None checks
gbrennon Jul 23, 2026
d30026f
docs(foundation): include Query subtype in Message base class docstring
gbrennon Jul 23, 2026
9182b04
fix(autohash): add missing return statement, close docstring, replace…
gbrennon Jul 23, 2026
9ddd613
refactor(value-object): replace _equality_components with @auto_hash
gbrennon Jul 23, 2026
d718bab
refactor(message): break Message from ValueObject inheritance
gbrennon Jul 23, 2026
e5c865b
refactor(subclass-vos): remove _equality_components from all ValueObj…
gbrennon Jul 23, 2026
bd6041a
test(equality): update tests after ValueObject/Message refactor
gbrennon Jul 23, 2026
15d53f9
refactor(autohash): decouple auto_hash from auto_freeze, apply indepe…
gbrennon Jul 23, 2026
a18e12e
docs: remove _equality_components references after ValueObject refactor
gbrennon Jul 23, 2026
8886130
docs: remove remaining _equality_components references from copilot i…
gbrennon Jul 23, 2026
8d1b218
fix(messages): apply auto_hash/auto_eq unconditionally in Message.__i…
gbrennon Jul 23, 2026
b543653
refactor(autohash): split __eq__ out of auto_hash into standalone aut…
gbrennon Jul 23, 2026
d46c147
refactor(foundation): integrate auto_eq, replace type: ignore with ca…
gbrennon Jul 23, 2026
2f9435b
test: remove type: ignore, use cast() for strict pyright compliance
gbrennon Jul 23, 2026
3d12c21
docs(autohash): standardize docstrings to match auto_freeze style
gbrennon Jul 23, 2026
7ecfcc6
docs(autoeq): standardize docstrings to match auto_freeze style
gbrennon Jul 23, 2026
bce1a5e
docs(guide): replace RST :func: roles with backtick code spans
gbrennon Jul 23, 2026
e67cec1
docs(guide): replace RST :func: roles with backtick code spans
gbrennon Jul 23, 2026
d4f99b2
docs(reference): replace RST :class: role with backtick code span
gbrennon Jul 23, 2026
cc21941
docs(aggregate-root): replace RST :meth: role with backtick code span
gbrennon Jul 23, 2026
77b58b3
docs(permissions): replace RST :class: role with backtick code span
gbrennon Jul 23, 2026
0f098fd
docs(permissions): replace RST :class: role with backtick code span
gbrennon Jul 23, 2026
cdbd206
docs(permissions): replace RST :class: role with backtick code span
gbrennon Jul 23, 2026
08d9572
docs(autoeq): replace RST :func: roles with backtick code spans
gbrennon Jul 23, 2026
09e6a02
docs(autofreeze): replace RST :func: role with backtick code span
gbrennon Jul 23, 2026
b6267e2
docs(autofreeze): replace RST :class:/:attr: roles with backtick code…
gbrennon Jul 23, 2026
9f89d44
docs(autohash): replace RST :func: roles with backtick code spans
gbrennon Jul 23, 2026
55aace5
docs(autohash): replace RST :func: roles with backtick code spans
gbrennon Jul 23, 2026
624206f
docs(autohash): replace RST :class: roles with backtick code spans
gbrennon Jul 23, 2026
b74f90d
docs(errors): replace RST :class: role with backtick code span
gbrennon Jul 23, 2026
b0b7dfc
docs(messages): replace RST :class: roles with backtick code spans
gbrennon Jul 23, 2026
9c2afa2
docs(messages): replace RST :func:/:class: roles with backtick code s…
gbrennon Jul 23, 2026
cd67590
docs(meta): replace RST :func: role with backtick code span
gbrennon Jul 23, 2026
7a01257
docs(value-object): replace RST :func: roles with backtick code spans
gbrennon Jul 23, 2026
f2fe12b
docs(aggregate-repository): replace RST :meth: role with backtick cod…
gbrennon Jul 23, 2026
cf75e51
docs(copilot-instructions): replace RST :func: roles with backtick co…
gbrennon Jul 23, 2026
24c1d71
docs(autofreeze): remove domain-specific Entity references from docst…
gbrennon Jul 23, 2026
c099e37
docs(reference): add auto-generated auto-eq and auto-hash reference p…
gbrennon Jul 23, 2026
528b7c5
docs(autofreeze): remove unused CantModifyImmutableAttributeError imp…
gbrennon Jul 23, 2026
a543af2
feat(scripts): add docstring import validation to autodoc generator
gbrennon Jul 23, 2026
9d81550
feat(presentation): remove unused LoggerPort import from middleware d…
gbrennon Jul 23, 2026
835b92a
fix(scripts): dedent code blocks when validating docstring imports
gbrennon Jul 23, 2026
e98ee26
refactor(domain): move ValueObject, Messages, and Specifications from…
gbrennon Jul 23, 2026
0bdce28
refactor(domain): update source imports from foundation to domain mod…
gbrennon Jul 23, 2026
5076b12
refactor(domain): update test imports from foundation to domain modules
gbrennon Jul 23, 2026
18abd5b
refactor(domain): update script imports from foundation to domain mod…
gbrennon Jul 23, 2026
e2f0f14
docs(domain): update reference pages and fix links for domain module …
gbrennon Jul 23, 2026
fad3210
fix(domain): lazify aggregate_root import to break circular dependency
gbrennon Jul 23, 2026
0d71784
docs: sync mkdocs nav entries with domain module move
gbrennon Jul 23, 2026
f4a9927
docs(foundation): create comprehensive auto-decorators reference page
gbrennon Jul 23, 2026
86da4e6
docs(foundation): remove individual auto decorator reference pages
gbrennon Jul 23, 2026
20adff6
docs: update cross-references to auto-decorators page
gbrennon Jul 23, 2026
3e13380
docs(foundation): clean auto-freeze module docstring examples
gbrennon Jul 23, 2026
1f92e5e
docs(foundation): clean auto-eq module docstrings and README
gbrennon Jul 23, 2026
1d8325e
docs(foundation): clean auto-hash module docstrings and README
gbrennon Jul 23, 2026
6fdc60a
chore(mkdocs): replace auto freeze to auto decorators
gbrennon Jul 23, 2026
2050a50
fix(auto-eq, auto-hash): use type.__setattr__ for attribute assignmen…
gbrennon Jul 23, 2026
f212692
fix(aggregate-repository): use UUID | None instead of unbounded TId
gbrennon Jul 23, 2026
eed0dfb
refactor(domain): keep TYPE_CHECKING + __getattr__ for circular impor…
gbrennon Jul 23, 2026
6d5fc58
test: add FieldResolver, DictMessageCodec, and domain init coverage t…
gbrennon Jul 23, 2026
de1f612
test(field-resolver): add coverage for string-form __slots__ conversion
gbrennon Jul 23, 2026
af8ab3c
test(decorators): add coverage for from_payload_fields, get_payload_f…
gbrennon Jul 23, 2026
aacb68d
refactor(foundation): remove concrete context value objects
gbrennon Jul 23, 2026
3b98a5a
refactor(domain): remove concrete permission checker implementations
gbrennon Jul 23, 2026
67dfe87
refactor: generify permission and port interfaces with domain type pa…
gbrennon Jul 23, 2026
25ecf50
style(domain): add explicit PermissionChecker inheritance to Composit…
gbrennon Jul 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ Do **not** implement `_payload` manually on decorated classes — the decorator
### ValueObject: Immutable Domain Values

```python
from forging_blocks.foundation.value_object import ValueObject
from collections.abc import Hashable
from forging_blocks.domain.value_object import ValueObject


class Email(ValueObject[str]):
__slots__ = ("_value",) # always include __slots__
Expand All @@ -181,13 +181,11 @@ class Email(ValueObject[str]):
@property
def value(self) -> str:
return self._value

@property
def _equality_components(self) -> tuple[Hashable, ...]:
return (self._value,) # controls equality and hashing
```

`ValueObject` uses `@auto_freeze` internally. After `__init__` completes, any mutation raises `CantModifyImmutableAttributeError`. Concrete subclasses are frozen automatically — no decorator needed.
Concrete ``ValueObject`` subclasses are automatically frozen and hashable via
`auto_freeze` and `auto_hash` (applied through ``__init_subclass__``).
After ``__init__`` completes, any mutation raises ``CantModifyImmutableAttributeError``.

### Specifications: Composable Predicates

Expand Down Expand Up @@ -459,7 +457,6 @@ class MyPort[T](OutboundPort):
| Blocking I/O in async context | ❌ Flag | Add `async def` / `await` |
| Manual `_payload` on `@event_dataclass` class | ❌ Flag | Decorator patches it automatically |
| Missing `__slots__` on ValueObject subclass | ❌ Flag | Add `__slots__ = ("_field",)` |
| Missing `_equality_components` on ValueObject | ❌ Flag | Implement required property |
| `try/except` for control flow | ❌ Flag | Use `Result[T, E]` |
| Overriding `@runtime_final` method | ❌ Flag | `TypeError` at class creation |
| Overriding `apply`, `replay`, `collect_events` in AggregateRoot | ❌ Flag | These are runtime-final |
Expand Down
18 changes: 7 additions & 11 deletions docs/guide/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class Task(Entity[int]):
return self
```

`Entity` uses **selective freezing** via `@auto_freeze(attrs=["_id"])` — the identity field (`_id`) is frozen after `__init__`, while other attributes remain mutable. This ensures the entity's identity never changes, while its state can evolve. See [Domain > Entities](../reference/domain/entities.md) for why identity matters, and [Foundation > Auto-freeze](../reference/foundation/auto-freeze.md) for the mechanism.
`Entity` uses **selective freezing** via `@auto_freeze(attrs=["_id"])` — the identity field (`_id`) is frozen after `__init__`, while other attributes remain mutable. This ensures the entity's identity never changes, while its state can evolve. See [Domain > Entities](../reference/domain/entities.md) for why identity matters, and [Foundation > Auto Decorators](../reference/foundation/auto-decorators.md) for the mechanism.

---

Expand Down Expand Up @@ -127,8 +127,7 @@ The design is:
## 4. Modeling a value with ValueObject

```python
from collections.abc import Hashable
from forging_blocks.foundation.value_object import ValueObject
from forging_blocks.domain.value_object import ValueObject


class Email(ValueObject[str]):
Expand All @@ -144,16 +143,13 @@ class Email(ValueObject[str]):
def value(self) -> str:
return self._value

@property
def _equality_components(self) -> tuple[Hashable, ...]:
return (self._value,)
```

`ValueObject` uses automatic freezing — subclasses need **no** `@auto_freeze`
decorator or `_freeze()` call. Two `Email` instances with the same value are
considered equal and can be used interchangeably as dictionary keys or set
members. Attempting to mutate one after construction raises a
`CantModifyImmutableAttributeError`.
`ValueObject` uses `auto_freeze` and `auto_hash` under the hood --
concrete subclasses are automatically frozen and hashable via
``__init_subclass__``. Two ``Email`` instances with the same value are
equal and can be used as dictionary keys or set members. Attempting to
mutate one after construction raises a ``CantModifyImmutableAttributeError``.

---

Expand Down
10 changes: 3 additions & 7 deletions docs/guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ When a value is more than a primitive, you can wrap it in a `ValueObject` to
make its rules visible and reusable.

```python
from collections.abc import Hashable
from forging_blocks.foundation.value_object import ValueObject
from forging_blocks.domain.value_object import ValueObject


class Email(ValueObject[str]):
Expand All @@ -65,14 +64,11 @@ class Email(ValueObject[str]):
@property
def value(self) -> str:
return self._value

@property
def _equality_components(self) -> tuple[Hashable, ...]:
return (self._value,)
```

`ValueObject` gives you value-based equality, hashability, and immutability
out of the box, so that you can focus on the rules of *your* value.
automatically via `auto_hash` and `auto_freeze`, so that you
can focus on the rules of *your* value.

---

Expand Down
5 changes: 4 additions & 1 deletion docs/reference/domain.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ The Domain block is the innermost ring. It imports nothing from outer layers. Wh
- **[Value Object](domain/value-objects.md)** — Immutable, defined by its values; prevents primitive obsession
- **[Aggregate Root](domain/aggregates.md)** — Consistency boundary; controls mutation and invariants
- **[Specification](domain/specifications.md)** — Composable predicates for business rules, querying, and validation
- **[Messages](domain/messages.md)** — Command, Event, Query (immutable, architecture-neutral)
- **[Domain Errors](domain/errors.md)** — Invalid states and rule violations in domain terms
- **[Validators](domain/validators.md)** — Concrete validation rules (RequiredValidator, EmailValidator, LengthValidator, RangeValidator)
- **[Permissions](domain/permissions.md)** — Composable permission checkers (RoleBased, ResourceBased, Composite)
- **[Permissions](domain/permissions.md)** — Composable permission checkers (protocol-based, application-defined)

---
## What it does not do
Expand All @@ -61,3 +62,5 @@ The Domain block is the innermost ring. It imports nothing from outer layers. Wh

!!! note "Specification"
A composable predicate over a candidate object for business rules, querying, and validation.
!!! note "Message"
An immutable dataclass representing a command, event, or query.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Messages are immutable, architecture-neutral data carriers — **Command**, **Event**, and **Query**.

All messages are frozen dataclasses with automatic serialization support (`to_dict`). Their type communicates intent: a Command asks for action, an Event records a fact, a Query requests data.
All messages are frozen dataclasses. Their type communicates intent: a Command asks for action, an Event records a fact, a Query requests data. Serialization is handled by codecs in the infrastructure layer (see `DictMessageCodec`).

## Types

Expand All @@ -12,7 +12,7 @@ All messages are frozen dataclasses with automatic serialization support (`to_di

## Message dataclass decorator

The `@message_dataclass` decorator creates boilerplate-free, frozen message types with automatic `to_dict()` and `from_dict()` support. Type aliases clarify intent:
The ``@message_dataclass`` decorator creates boilerplate-free, frozen message types. Type aliases clarify intent:

- `@command_dataclass` — For commands
- `@event_dataclass` — For domain events
Expand All @@ -22,7 +22,7 @@ All aliases are the same decorator; the name signals intent. Instances are froze

## When to use

Annotate a class with `@command_dataclass`, `@event_dataclass`, or `@query_dataclass`. The decorator handles freezing, `to_dict()`, and `from_dict()`. Choose the alias that matches the message's role — command for intent, event for facts, query for data requests.
Annotate a class with ``@command_dataclass``, ``@event_dataclass``, or ``@query_dataclass``. The decorator handles freezing and wires up reconstruction via ``from_payload_fields``. Choose the alias that matches the message's role — command for intent, event for facts, query for data requests.

!!! note "Related"
See [Application Use Cases & Handlers](../application/use-cases.md) for how messages are processed.
58 changes: 29 additions & 29 deletions docs/reference/domain/permissions.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
# Permissions

Composable permission-checking strategies for authorization decisions. Each checker evaluates an `AuthorizationContext` against a specific `Permission` and returns `True` when granted.
Composable permission-checking strategies for authorization decisions. Each checker evaluates an application-defined context against a specific `Permission` and returns `True` when granted.

## PermissionChecker

`PermissionChecker` is a `Protocol` that any permission-checking implementation must satisfy.
`PermissionChecker[PermissionCheckContext]` is a `Protocol` that any permission-checking implementation must satisfy. The context type is provided by the application.

```python
class PermissionChecker(Protocol):
async def check(self, context: AuthorizationContext, permission: Permission) -> bool:
class PermissionChecker[PermissionCheckContext](Protocol):
async def check(self, context: PermissionCheckContext, permission: Permission) -> bool:
...
```

Implementations may internally use synchronous logic, but must expose an `async def check(...)` method — callers always `await` the result.

## RoleBasedPermissionChecker
## CompositePermissionChecker

Grants permissions based on a static mapping of roles to allowed permissions. Looks up the user's roles in the `AuthorizationContext` and checks whether any assigned role includes the requested permission.
Combines multiple `PermissionChecker[PermissionCheckContext]` instances with OR logic. A check passes as soon as **any** inner checker approves. Returns `True` immediately on the first success; otherwise `False` after all checkers have been consulted.

```python
checker = RoleBasedPermissionChecker({
"admin": [Permission.READ, Permission.WRITE, Permission.DELETE, Permission.ADMIN],
"editor": [Permission.READ, Permission.WRITE],
})
checker = CompositePermissionChecker([
my_role_checker,
my_resource_checker,
])
result = await checker.check(context, Permission.READ)
```

## ResourcePermissionChecker
## Designing your own checkers

Grants permissions based on a static mapping of resource types to allowed permissions. Inspects `AuthorizationContext.resource_type` and checks whether the targeted resource type permits the requested permission.
Applications define concrete `PermissionChecker` implementations that inspect their own context type. A role-based checker might look up permissions from a role-to-permission mapping:

```python
checker = ResourcePermissionChecker({
"document": [Permission.READ, Permission.WRITE],
"image": [Permission.READ],
})
from forging_blocks.domain.permissions import PermissionChecker

class RoleBasedChecker[PermissionCheckContext](PermissionChecker[PermissionCheckContext]):
def __init__(self, role_map: dict[str, list[Permission]]) -> None:
self._role_map = role_map

async def check(self, context: PermissionCheckContext, permission: Permission) -> bool:
roles = getattr(context, "roles", [])
for role in roles:
if permission in self._role_map.get(role, []):
return True
return False
```

## CompositePermissionChecker

Combines multiple `PermissionChecker` instances with OR logic. A check passes as soon as **any** inner checker approves. Returns `True` immediately on the first success; otherwise `False` after all checkers have been consulted.

```python
checker = CompositePermissionChecker([
RoleBasedPermissionChecker({"admin": [Permission.READ]}),
ResourcePermissionChecker({"document": [Permission.READ]}),
])
```
A resource-based checker would similarly inspect resource metadata on the context object.

## When to use

Use `RoleBasedPermissionChecker` for role-driven authorization (RBAC). Use `ResourcePermissionChecker` for resource-level access control. Combine them with `CompositePermissionChecker` when authorization depends on multiple factors — an admin role OR ownership of a document, for example.
Use `CompositePermissionChecker` to combine multiple checkers when authorization depends on multiple factors — an admin role OR ownership of a document, for example. Define application-specific `PermissionChecker` subclasses for role-driven authorization (RBAC), resource-level access control, or any custom authorization logic.

All checkers operate on the foundation `AuthorizationContext` and `Permission` types, keeping the domain free of infrastructure concerns.
All checkers operate on the foundation `Permission` type and an application-defined context, keeping the domain free of infrastructure concerns.

!!! note "Related"
See [Foundation Context](../foundation/context.md) for `AuthorizationContext` and `Permission`, and [Foundation Errors](../foundation/errors.md) for error types.
Permissions use the `Permission` enum (in `forging_blocks.foundation.permission`). See [Foundation Errors](../foundation/errors.md) for error types.
2 changes: 1 addition & 1 deletion docs/reference/domain/specifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ A `Specification` encapsulates a rule evaluated with `is_satisfied_by(candidate)
Subclass `Specification` and implement `is_satisfied_by(candidate) → bool`. Compose with `&`, `|`, `~` instead of nesting if-statements. Each specification is a single, testable unit of logic.

!!! note "Where the implementation lives"
The specification pattern is defined in the [Foundation](../foundation.md) block because composable predicates are reusable outside the Domain block. The Domain block re-exports it.
The specification pattern is defined in the Domain block alongside Entity and AggregateRoot. It is imported from `forging_blocks.domain.specification`.
2 changes: 1 addition & 1 deletion docs/reference/domain/value-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ Inherit from `ValueObject` when you need immutability, value-based equality, and
Value Objects prevent domain rules from being scattered across the codebase. Wrapping meaning in explicit types makes constraints visible, reusable, and testable.

!!! note "Where the implementation lives"
The `ValueObject` base class lives in the [Foundation](../foundation.md) block because value-based equality and immutability are reusable outside the Domain block.
The `ValueObject` base class lives in the Domain block alongside Entity and AggregateRoot. It is imported from `forging_blocks.domain.value_object`.
19 changes: 2 additions & 17 deletions docs/reference/foundation.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ Each abstraction serves one focused purpose:
- `Result` replaces exceptions for predictable control flow.
- `Port` defines boundaries as protocols — what is expected, not how.
- `Error` gives structure to failure with messages and metadata.
- `ValueObject` adds immutability, value equality, and hashing.
- `Specification` composes business rules into testable predicates.

These are not patterns you must use everywhere. They are tools you reach for when plain Python types stop communicating intent clearly enough.

Expand All @@ -27,7 +25,6 @@ Start with the abstractions that give the most immediate value:

1. **`Result`** — Replace functions that return `None` on failure or raise exceptions for control flow. Return `Ok(value)` or `Err(error)` instead.
2. **`Port`** — Define a protocol for any dependency you might swap later: repositories, event buses, loggers.
3. **`ValueObject`** — Wrap primitive values when validation logic is scattered across functions.

The Foundation block is pure Python — standard library only. It introduces no framework dependencies.

Expand All @@ -37,14 +34,11 @@ The Foundation block is pure Python — standard library only. It introduces no
- **[Result](foundation/result.md)** — Explicit Ok/Err outcomes without exceptions for control flow
- **[Ports](foundation/ports.md)** — Boundaries between components (InboundPort, OutboundPort)
- **[Errors](foundation/errors.md)** — Structured error model (message + metadata, validation, rule violation, combined)
- **[Messages](foundation/messages.md)** — Command, Event, Query (immutable, architecture-neutral)
- **[Value Objects](foundation/value-objects.md)** — Immutable, value-based equality, hashing
- **[Auto-Freeze](foundation/auto-freeze.md)** — Lightweight immutability without inheriting from ValueObject
- **[Specifications](foundation/specifications.md)** — Composable predicates (and/or/not)
- **[Auto Decorators](foundation/auto-decorators.md)** — Immutability, equality, and hashing decorators (auto_freeze, auto_eq, auto_hash)
- **[Mappers](foundation/mappers.md)** — Explicit transformations between types
- **[Identified](foundation/identified.md)** — Protocol for objects carrying an identifier
- **[Meta Utilities](foundation/meta.md)** — Runtime enforcement (final, sealed, abstract)
- **[Context](foundation/context.md)** — Immutable context objects (ServiceContext, AuthorizationContext, TransactionContext)

- **[Rules](foundation/rules.md)** — Composable validation rules (ValidationRule)

---
Expand All @@ -66,12 +60,3 @@ The Foundation block is pure Python — standard library only. It introduces no

!!! note "Error"
A structured failure with a message and metadata, used across all layers.

!!! note "Message"
An immutable dataclass representing a command, event, or query.

!!! note "ValueObject"
An immutable object with value-based equality and hashing.

!!! note "Specification"
A composable predicate over a candidate object for rules, validation, and filtering.
Loading
Loading