Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spakky_domain-6.1.1.tar.gz (7.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

spakky_domain-6.1.1-py3-none-any.whl (12.3 kB view details)

Uploaded Python 3

File details

Details for the file spakky_domain-6.1.1.tar.gz.

File metadata

  • Download URL: spakky_domain-6.1.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

Hashes for spakky_domain-6.1.1.tar.gz
Algorithm Hash digest
SHA256 6e51a1bc257b4aa63bd8e4407e1f0f4f28dbba5876a36682453327d7a5af84e5
MD5 1eb061ed475ecf18de9b2c199d929b78
BLAKE2b-256 8310c0684b6ae73ea444313474e2a583bc225b5cd13cf5cad669f55bced90e44

See more details on using hashes here.

Provenance

The following attestation bundles were made for spakky_domain-6.1.1.tar.gz:

Publisher: release.yml on E5presso/spakky-framework

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file spakky_domain-6.1.1-py3-none-any.whl.

File metadata

  • Download URL: spakky_domain-6.1.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

Hashes for spakky_domain-6.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7a72c7d5f92bab2068c7bea90a3d80409ca3b0c5377c65f771569c79430f7f82
MD5 31d971b0a3c3efb742dacd9452fb8f67
BLAKE2b-256 838f5b55827d0a83bc864f78aad546decb45bdd295b22be17bd8b841e5faf1a4

See more details on using hashes here.

Provenance

The following attestation bundles were made for spakky_domain-6.1.1-py3-none-any.whl:

Publisher: release.yml on E5presso/spakky-framework

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page