DDD building blocks for Spakky Framework (Entity, AggregateRoot, ValueObject, DomainEvent, CQRS)
Project description
Spakky DDD
Domain-Driven Design building blocks for Spakky Framework.
Installation
pip install spakky-domain
Features
- Entities: Objects with unique identity and lifecycle
- Value Objects: Immutable objects compared by attributes
- Aggregate Roots: Consistency boundaries with event management
- Domain Events: Immutable events for event-driven architecture
- CQRS: Command and Query use case abstractions
Quick Start
Entity
Entities are objects with a unique identity that persists over time:
from uuid import UUID, uuid4
from spakky.core.common.mutability import mutable
from spakky.domain.models.entity import AbstractEntity
@mutable
class User(AbstractEntity[UUID]):
name: str
email: str
@classmethod
def next_id(cls) -> UUID:
return uuid4()
def validate(self) -> None:
if not self.email:
raise ValueError("Email is required")
Value Object
Value Objects are immutable and compared by their attributes:
from spakky.core.common.mutability import immutable
from spakky.domain.models.value_object import AbstractValueObject
@immutable
class Money(AbstractValueObject):
amount: int
currency: str
def validate(self) -> None:
if self.amount < 0:
raise ValueError("Amount cannot be negative")
Aggregate Root
Aggregate Roots are entities that manage domain events:
from uuid import UUID, uuid4
from spakky.core.common.mutability import mutable, immutable
from spakky.domain.models.aggregate_root import AbstractAggregateRoot
from spakky.domain.models.event import AbstractDomainEvent
@immutable
class OrderCreatedEvent(AbstractDomainEvent):
order_id: UUID
customer_id: UUID
@mutable
class Order(AbstractAggregateRoot[UUID]):
customer_id: UUID
total: int
@classmethod
def next_id(cls) -> UUID:
return uuid4()
def validate(self) -> None:
if self.total < 0:
raise ValueError("Total cannot be negative")
@classmethod
def create(cls, customer_id: UUID, total: int) -> "Order":
order = cls(uid=cls.next_id(), customer_id=customer_id, total=total)
order.add_event(OrderCreatedEvent(order_id=order.uid, customer_id=customer_id))
return order
Domain Events
Domain events represent state changes in the domain:
from spakky.core.common.mutability import immutable
from spakky.domain.models.event import AbstractDomainEvent, AbstractIntegrationEvent
# Internal domain event
@immutable
class UserRegistered(AbstractDomainEvent):
user_id: str
email: str
# Cross-boundary integration event
@immutable
class UserRegisteredIntegration(AbstractIntegrationEvent):
user_id: str
email: str
CQRS Use Cases
Separate read and write operations.
Key Principles:
- Commands: Use Repository for domain aggregate persistence
- Queries: Implement directly using ORM/SQL (do NOT add query methods to Repository)
- This separation prevents domain pollution by keeping query concerns out of the domain layer
from uuid import UUID
from spakky.core.common.mutability import immutable
from spakky.domain.application.command import AbstractCommand, IAsyncCommandUseCase
from spakky.domain.application.query import AbstractQuery, IAsyncQueryUseCase
# Command
@immutable
class CreateUserCommand(AbstractCommand):
name: str
email: str
class CreateUserUseCase(IAsyncCommandUseCase[CreateUserCommand, UUID]):
async def run(self, command: CreateUserCommand) -> UUID:
# Business logic here
...
# Query
@immutable
class GetUserQuery(AbstractQuery):
user_id: UUID
class GetUserUseCase(IAsyncQueryUseCase[GetUserQuery, User | None]):
async def run(self, query: GetUserQuery) -> User | None:
# Business logic here
...
API Reference
Models
| Class | Description |
|---|---|
AbstractEntity[T] |
Base class for entities with identity type T |
AbstractAggregateRoot[T] |
Entity that manages domain events |
AbstractValueObject |
Immutable value object |
AbstractEvent |
Base class for all events |
AbstractDomainEvent |
Domain events (within bounded context) |
AbstractIntegrationEvent |
Integration events (cross-boundary) |
Application
| Class | Description |
|---|---|
AbstractCommand |
Base class for command DTOs |
AbstractQuery |
Base class for query DTOs |
ICommandUseCase |
Sync command use case interface |
IAsyncCommandUseCase |
Async command use case interface |
IQueryUseCase |
Sync query use case interface |
IAsyncQueryUseCase |
Async query use case interface |
Related Packages
| Package | Description |
|---|---|
spakky-data |
Repository and transaction abstractions |
spakky-event |
Event publisher/consumer interfaces and @EventHandler stereotype |
License
MIT License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spakky_domain-6.3.1.tar.gz.
File metadata
- Download URL: spakky_domain-6.3.1.tar.gz
- Upload date:
- Size: 7.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ca0649d05ba787ca2308716a3641a8691aa0c4e6e671924ff83f7b6865805de
|
|
| MD5 |
65d5926dd2a4ac3c54fef6e1bdcb3873
|
|
| BLAKE2b-256 |
f50e15a9aa80b10f148926a4fc87f70c0f1304e0d36e076ef2bbaa02977a7e1e
|
Provenance
The following attestation bundles were made for spakky_domain-6.3.1.tar.gz:
Publisher:
release.yml on E5presso/spakky-framework
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spakky_domain-6.3.1.tar.gz -
Subject digest:
2ca0649d05ba787ca2308716a3641a8691aa0c4e6e671924ff83f7b6865805de - Sigstore transparency entry: 1236503369
- Sigstore integration time:
-
Permalink:
E5presso/spakky-framework@6d8469ad090c00a829421ab05aafc6d10178d93f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/E5presso
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6d8469ad090c00a829421ab05aafc6d10178d93f -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file spakky_domain-6.3.1-py3-none-any.whl.
File metadata
- Download URL: spakky_domain-6.3.1-py3-none-any.whl
- Upload date:
- Size: 12.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d20e14b94e01d5bae73a091f3cfd8e4b3ed7ff9c6230ec56a203d566083bf35
|
|
| MD5 |
1a8507ad7cd445f0b49b5b75db76365e
|
|
| BLAKE2b-256 |
c01b9c3e6f14cdc6baccd35c4368ed54ca08fdcd749a412882abb445da6b1b1d
|
Provenance
The following attestation bundles were made for spakky_domain-6.3.1-py3-none-any.whl:
Publisher:
release.yml on E5presso/spakky-framework
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spakky_domain-6.3.1-py3-none-any.whl -
Subject digest:
2d20e14b94e01d5bae73a091f3cfd8e4b3ed7ff9c6230ec56a203d566083bf35 - Sigstore transparency entry: 1236503372
- Sigstore integration time:
-
Permalink:
E5presso/spakky-framework@6d8469ad090c00a829421ab05aafc6d10178d93f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/E5presso
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6d8469ad090c00a829421ab05aafc6d10178d93f -
Trigger Event:
workflow_dispatch
-
Statement type: