콘텐츠로 이동

spakky-data

Repository, Transaction 추상화 — 데이터 접근 계층

spakky-data는 core 레이어의 추상 계약만 제공합니다. 애플리케이션에서 SQLAlchemy로 영속화를 구현하려면 spakky-sqlalchemy를 함께 설치하고, AbstractMappableTable / AbstractAsyncGenericRepository 또는 AbstractGenericRepository를 사용하세요.

사용자 흐름은 데이터베이스 가이드를 먼저 보는 편이 좋습니다. 이 페이지는 실제 API 표면을 확인하기 위한 레퍼런스입니다.

스테레오타입

spakky.data.stereotype.repository

Repository stereotype for data access layer.

This module provides @Repository stereotype for organizing classes that handle data persistence and retrieval.

Repository(*, name='', scope=Scope.SINGLETON) dataclass

Bases: Pod

Stereotype for repository classes handling data access.

Repositories provide an abstraction over data sources, implementing data access patterns and queries.

options: show_root_heading: false

영속성

spakky.data.persistency.repository

EntityNotFoundError

Bases: AbstractSpakkyDomainError

Raised when an entity cannot be found by the given identifier.

VersionConflictError

Bases: AbstractSpakkyDomainError

Raised when optimistic locking detects a version conflict during save.

IGenericRepository

Bases: ABC

Synchronous generic repository interface for aggregate persistence.

get(aggregate_id) abstractmethod

Get an aggregate by ID.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
def get(self, aggregate_id: AggregateIdT_contra) -> AggregateRootT:
    """Get an aggregate by ID."""
    ...

get_or_none(aggregate_id) abstractmethod

Get an aggregate by ID, or None if not found.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
def get_or_none(self, aggregate_id: AggregateIdT_contra) -> AggregateRootT | None:
    """Get an aggregate by ID, or None if not found."""
    ...

contains(aggregate_id) abstractmethod

Check if an aggregate with the given ID exists.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
def contains(self, aggregate_id: AggregateIdT_contra) -> bool:
    """Check if an aggregate with the given ID exists."""
    ...

range(aggregate_ids) abstractmethod

Get multiple aggregates by their IDs.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
def range(
    self, aggregate_ids: Sequence[AggregateIdT_contra]
) -> Sequence[AggregateRootT]:
    """Get multiple aggregates by their IDs."""
    ...

save(aggregate) abstractmethod

Persist an aggregate and return the saved instance.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
def save(self, aggregate: AggregateRootT) -> AggregateRootT:
    """Persist an aggregate and return the saved instance."""
    ...

save_all(aggregates) abstractmethod

Persist multiple aggregates and return the saved instances.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
def save_all(
    self, aggregates: Sequence[AggregateRootT]
) -> Sequence[AggregateRootT]:
    """Persist multiple aggregates and return the saved instances."""
    ...

delete(aggregate) abstractmethod

Delete an aggregate and return the deleted instance.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
def delete(self, aggregate: AggregateRootT) -> AggregateRootT:
    """Delete an aggregate and return the deleted instance."""
    ...

delete_all(aggregates) abstractmethod

Delete multiple aggregates and return the deleted instances.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
def delete_all(
    self, aggregates: Sequence[AggregateRootT]
) -> Sequence[AggregateRootT]:
    """Delete multiple aggregates and return the deleted instances."""
    ...

IAsyncGenericRepository

Bases: ABC

Asynchronous generic repository interface for aggregate persistence.

get(aggregate_id) abstractmethod async

Get an aggregate by ID.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
async def get(self, aggregate_id: AggregateIdT_contra) -> AggregateRootT:
    """Get an aggregate by ID."""
    ...

get_or_none(aggregate_id) abstractmethod async

Get an aggregate by ID, or None if not found.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
async def get_or_none(
    self, aggregate_id: AggregateIdT_contra
) -> AggregateRootT | None:
    """Get an aggregate by ID, or None if not found."""
    ...

contains(aggregate_id) abstractmethod async

Check if an aggregate with the given ID exists.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
async def contains(self, aggregate_id: AggregateIdT_contra) -> bool:
    """Check if an aggregate with the given ID exists."""
    ...

range(aggregate_ids) abstractmethod async

Get multiple aggregates by their IDs.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
async def range(
    self, aggregate_ids: Sequence[AggregateIdT_contra]
) -> Sequence[AggregateRootT]:
    """Get multiple aggregates by their IDs."""
    ...

save(aggregate) abstractmethod async

Persist an aggregate and return the saved instance.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
async def save(self, aggregate: AggregateRootT) -> AggregateRootT:
    """Persist an aggregate and return the saved instance."""
    ...

save_all(aggregates) abstractmethod async

Persist multiple aggregates and return the saved instances.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
async def save_all(
    self, aggregates: Sequence[AggregateRootT]
) -> Sequence[AggregateRootT]:
    """Persist multiple aggregates and return the saved instances."""
    ...

delete(aggregate) abstractmethod async

Delete an aggregate and return the deleted instance.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
async def delete(self, aggregate: AggregateRootT) -> AggregateRootT:
    """Delete an aggregate and return the deleted instance."""
    ...

delete_all(aggregates) abstractmethod async

Delete multiple aggregates and return the deleted instances.

Source code in core/spakky-data/src/spakky/data/persistency/repository.py
@abstractmethod
async def delete_all(
    self, aggregates: Sequence[AggregateRootT]
) -> Sequence[AggregateRootT]:
    """Delete multiple aggregates and return the deleted instances."""
    ...

options: show_root_heading: false

spakky.data.persistency.transaction

AbstractTransaction(autocommit=True)

Bases: IDisposable, ABC

Abstract base for synchronous transaction context managers.

Initialize the transaction.

Parameters:

Name Type Description Default
autocommit bool

Whether to auto-commit on successful exit.

True
Source code in core/spakky-data/src/spakky/data/persistency/transaction.py
def __init__(self, autocommit: bool = True) -> None:
    """Initialize the transaction.

    Args:
        autocommit: Whether to auto-commit on successful exit.
    """
    self.autocommit_enabled = autocommit

initialize() abstractmethod

Initialize the transaction (e.g. open a session).

Source code in core/spakky-data/src/spakky/data/persistency/transaction.py
@abstractmethod
def initialize(self) -> None:
    """Initialize the transaction (e.g. open a session)."""
    ...

dispose() abstractmethod

Dispose the transaction resources (e.g. close a session).

Source code in core/spakky-data/src/spakky/data/persistency/transaction.py
@abstractmethod
def dispose(self) -> None:
    """Dispose the transaction resources (e.g. close a session)."""
    ...

commit() abstractmethod

Commit the current transaction.

Source code in core/spakky-data/src/spakky/data/persistency/transaction.py
@abstractmethod
def commit(self) -> None:
    """Commit the current transaction."""
    ...

rollback() abstractmethod

Roll back the current transaction.

Source code in core/spakky-data/src/spakky/data/persistency/transaction.py
@abstractmethod
def rollback(self) -> None:
    """Roll back the current transaction."""
    ...

AbstractAsyncTransaction(autocommit=True)

Bases: IAsyncDisposable, ABC

Abstract base for asynchronous transaction context managers.

Initialize the async transaction.

Parameters:

Name Type Description Default
autocommit bool

Whether to auto-commit on successful exit.

True
Source code in core/spakky-data/src/spakky/data/persistency/transaction.py
def __init__(self, autocommit: bool = True) -> None:
    """Initialize the async transaction.

    Args:
        autocommit: Whether to auto-commit on successful exit.
    """
    self.autocommit_enabled = autocommit

initialize() abstractmethod async

Initialize the async transaction (e.g. open a session).

Source code in core/spakky-data/src/spakky/data/persistency/transaction.py
@abstractmethod
async def initialize(self) -> None:
    """Initialize the async transaction (e.g. open a session)."""
    ...

dispose() abstractmethod async

Dispose the async transaction resources.

Source code in core/spakky-data/src/spakky/data/persistency/transaction.py
@abstractmethod
async def dispose(self) -> None:
    """Dispose the async transaction resources."""
    ...

commit() abstractmethod async

Commit the current async transaction.

Source code in core/spakky-data/src/spakky/data/persistency/transaction.py
@abstractmethod
async def commit(self) -> None:
    """Commit the current async transaction."""
    ...

rollback() abstractmethod async

Roll back the current async transaction.

Source code in core/spakky-data/src/spakky/data/persistency/transaction.py
@abstractmethod
async def rollback(self) -> None:
    """Roll back the current async transaction."""
    ...

options: show_root_heading: false

spakky.data.persistency.aggregate_collector

AggregateCollector()

Context-scoped collector for tracking aggregates saved in a transaction.

Source code in core/spakky-data/src/spakky/data/persistency/aggregate_collector.py
def __init__(self) -> None:
    self._aggregates = []

collect(aggregate)

Track an aggregate that was saved during the transaction.

This method is called by Repository implementations after saving an aggregate to the database. The collector stores a reference to track which aggregates were modified.

Parameters:

Name Type Description Default
aggregate AbstractAggregateRoot

The aggregate to track.

required
Example

collector = AggregateCollector() user = User.create(name="Alice") collector.collect(user)

Source code in core/spakky-data/src/spakky/data/persistency/aggregate_collector.py
def collect(self, aggregate: AbstractAggregateRoot) -> None:
    """Track an aggregate that was saved during the transaction.

    This method is called by Repository implementations after saving
    an aggregate to the database. The collector stores a reference
    to track which aggregates were modified.

    Args:
        aggregate: The aggregate to track.

    Example:
        >>> collector = AggregateCollector()
        >>> user = User.create(name="Alice")
        >>> collector.collect(user)
    """
    self._aggregates.append(aggregate)

all()

Get all tracked aggregates.

Returns a sequence of all aggregates collected during the current transaction. This is typically called by aspects after the use case logic completes to extract domain events.

Returns:

Type Description
Sequence[AbstractAggregateRoot]

Sequence of tracked aggregates.

Example

collector = AggregateCollector() user = User.create(name="Bob") collector.collect(user) aggregates = collector.get_all() len(aggregates) 1

Source code in core/spakky-data/src/spakky/data/persistency/aggregate_collector.py
def all(self) -> Sequence[AbstractAggregateRoot]:
    """Get all tracked aggregates.

    Returns a sequence of all aggregates collected during the current
    transaction. This is typically called by aspects after the use case
    logic completes to extract domain events.

    Returns:
        Sequence of tracked aggregates.

    Example:
        >>> collector = AggregateCollector()
        >>> user = User.create(name="Bob")
        >>> collector.collect(user)
        >>> aggregates = collector.get_all()
        >>> len(aggregates)
        1
    """
    return list(self._aggregates)

clear()

Clear all tracked aggregates.

This method is called after events have been published or when a transaction completes (success or failure) to prepare the collector for the next transaction.

Example

collector = AggregateCollector() user = User.create(name="Charlie") collector.collect(user) collector.clear() len(collector.get_all()) 0

Source code in core/spakky-data/src/spakky/data/persistency/aggregate_collector.py
def clear(self) -> None:
    """Clear all tracked aggregates.

    This method is called after events have been published or when
    a transaction completes (success or failure) to prepare the
    collector for the next transaction.

    Example:
        >>> collector = AggregateCollector()
        >>> user = User.create(name="Charlie")
        >>> collector.collect(user)
        >>> collector.clear()
        >>> len(collector.get_all())
        0
    """
    self._aggregates.clear()

options: show_root_heading: false

spakky.data.persistency.error

AbstractSpakkyPersistencyError

Bases: AbstractSpakkyFrameworkError, ABC

Base error for persistency layer operations.

options: show_root_heading: false

외부 Proxy

spakky.data.external.proxy

ProxyModel

Immutable read-only projection model identified by an equatable ID.

__eq__(other)

Compare by ID equality.

Source code in core/spakky-data/src/spakky/data/external/proxy.py
def __eq__(self, other: object) -> bool:
    """Compare by ID equality."""
    if not isinstance(other, type(self)):
        return False
    return self.id == other.id

__hash__()

Hash based on ID.

Source code in core/spakky-data/src/spakky/data/external/proxy.py
def __hash__(self) -> int:
    """Hash based on ID."""
    return hash(self.id)

IGenericProxy

Bases: ABC

Synchronous read-only proxy interface for querying projections.

get(proxy_id) abstractmethod

Get a proxy model by ID.

Source code in core/spakky-data/src/spakky/data/external/proxy.py
@abstractmethod
def get(self, proxy_id: ProxyIdT_contra) -> ProxyModelT_co:
    """Get a proxy model by ID."""
    ...

get_or_none(proxy_id) abstractmethod

Get a proxy model by ID, or None if not found.

Source code in core/spakky-data/src/spakky/data/external/proxy.py
@abstractmethod
def get_or_none(self, proxy_id: ProxyIdT_contra) -> ProxyModelT_co | None:
    """Get a proxy model by ID, or None if not found."""
    ...

contains(proxy_id) abstractmethod

Check if a proxy model with the given ID exists.

Source code in core/spakky-data/src/spakky/data/external/proxy.py
@abstractmethod
def contains(self, proxy_id: ProxyIdT_contra) -> bool:
    """Check if a proxy model with the given ID exists."""
    ...

range(proxy_ids) abstractmethod

Get multiple proxy models by their IDs.

Source code in core/spakky-data/src/spakky/data/external/proxy.py
@abstractmethod
def range(self, proxy_ids: Sequence[ProxyIdT_contra]) -> Sequence[ProxyModelT_co]:
    """Get multiple proxy models by their IDs."""
    ...

IAsyncGenericProxy

Bases: ABC

Asynchronous read-only proxy interface for querying projections.

get(proxy_id) abstractmethod async

Get a proxy model by ID.

Source code in core/spakky-data/src/spakky/data/external/proxy.py
@abstractmethod
async def get(self, proxy_id: ProxyIdT_contra) -> ProxyModelT_co:
    """Get a proxy model by ID."""
    ...

get_or_none(proxy_id) abstractmethod async

Get a proxy model by ID, or None if not found.

Source code in core/spakky-data/src/spakky/data/external/proxy.py
@abstractmethod
async def get_or_none(self, proxy_id: ProxyIdT_contra) -> ProxyModelT_co | None:
    """Get a proxy model by ID, or None if not found."""
    ...

contains(proxy_id) abstractmethod async

Check if a proxy model with the given ID exists.

Source code in core/spakky-data/src/spakky/data/external/proxy.py
@abstractmethod
async def contains(self, proxy_id: ProxyIdT_contra) -> bool:
    """Check if a proxy model with the given ID exists."""
    ...

range(proxy_ids) abstractmethod async

Get multiple proxy models by their IDs.

Source code in core/spakky-data/src/spakky/data/external/proxy.py
@abstractmethod
async def range(
    self, proxy_ids: Sequence[ProxyIdT_contra]
) -> Sequence[ProxyModelT_co]:
    """Get multiple proxy models by their IDs."""
    ...

options: show_root_heading: false

spakky.data.external.error

AbstractSpakkyExternalError

Bases: AbstractSpakkyFrameworkError, ABC

Base error for external proxy operations.

options: show_root_heading: false

Aspect

spakky.data.aspects.transactional

Transactional() dataclass

Bases: FunctionAnnotation

Annotation for marking methods as transactional.

Methods decorated with @Transactional() will be executed within a transaction context, ensuring atomicity of operations.

AsyncTransactionalAspect(transaction)

Bases: IAsyncAspect

Aspect for managing transactions in async methods.

Intercepts async methods decorated with @Transactional and ensures that they are executed within a transaction context.

Source code in core/spakky-data/src/spakky/data/aspects/transactional.py
def __init__(self, transaction: AbstractAsyncTransaction) -> None:
    self._transaction = transaction

TransactionalAspect(transaction)

Bases: IAspect

Aspect for managing transactions in sync methods.

Intercepts sync methods decorated with @Transactional and ensures that they are executed within a transaction context.

Source code in core/spakky-data/src/spakky/data/aspects/transactional.py
def __init__(self, transaction: AbstractTransaction) -> None:
    self._transaction = transaction

transactional(func)

Decorator for marking methods as transactional.

Parameters:

Name Type Description Default
func Callable[P, R]

The function to decorate.

required

Returns: The decorated function with transactional annotation.

Source code in core/spakky-data/src/spakky/data/aspects/transactional.py
def transactional[**P, R](func: Callable[P, R]) -> Callable[P, R]:
    """Decorator for marking methods as transactional.

    Args:
        func: The function to decorate.
    Returns:
        The decorated function with transactional annotation.
    """
    return Transactional()(func)

options: show_root_heading: false