Skip to main content

🧱 DDD for python

モジュラモノリス + ドメイン駆動設計のカーネル。業務語彙をひとつも持たないことを設計制約にしている。

アーキテクチャ契約の検査は clean-architecture が担う。 このリポジトリは「import できるカーネル」だけを配る。

入っているもの

提供するもの
合成 AppModule / CompositeModule — モジュールの宣言点と合成ルート
集約 DomainEvent / DomainEventPublisher / DomainEventSubscriber / DomainRegistry
ユースケース UnitOfWork / ApplicationServiceLifeCycle / @transactional
outbox StoredEvent / EventStore / EventContextProvider
inbox ConsumedNotification / ConsumedNotificationStore / MessageSubscriber
発行 Notification / NotificationPublisher / PublishedNotificationTracker
アダプタ InMem*(テスト用) / SQLAlchemyUnitOfWork(extras: sqlalchemy
適合テスト ddd4py.common.testing.verify_* — 自分のアダプタ実装が契約を満たすか検証する

導入

uv add ddd4py
uv add "ddd4py[sqlalchemy]"   # SQLAlchemy アダプタも使う場合

配布名は ddd4py、import 名は ddd4py.<module> になる。今あるモジュールは ddd4py.common (業務語彙を持たないカーネル本体)だけで、ddd4py 直下には __version__ しか置かない。 common 以外のモジュールを足しても import 名が衝突しないための空間の取り方。

from ddd4py.common import DomainEvent, UnitOfWork, transactional
from ddd4py.common.port.adapter.persistence.inmem import InMemEventStore

トランザクションと配送の保証

@transactional の内側で publish されたドメインイベントは、集約の更新と同一トランザクションで outbox(StoredEvent)に追記される。ネストした境界は最外に join し、内側で失敗すれば最外まで巻き戻る (部分 commit を許さない)。

受信側は MessageSubscriber._dispatch が listener ごとにトランザクションを開始し、 consumed marker の INSERT(claim-before-process)と listener の副作用を単一トランザクションで commit する (Idempotent Consumer / transactional inbox)。at-least-once の重複は marker の unique 制約が先勝ち 1 つに絞る。

実行文脈というただ 1 つの拡張点

カーネルは「いま誰のリクエストを処理しているか」を知らない。知っているのは、outbox にそれを刻印する ことと、受信時にそれを確立することだけ。

from ddd4py.common import EventContext, EventContextProvider

class TenantContextProvider(EventContextProvider):
    def current(self) -> EventContext:
        tenant = CurrentTenant.get()
        return EventContext(partition_key=tenant.id, payload=tenant.to_dict())

    @contextmanager
    def bind(self, context: EventContext) -> Iterator[None]:
        with CurrentTenant.of(context.payload).bind():
            yield

単一テナントなら NullEventContextProvider のままでよい。

案件ごとの差し替え

CompositeModule は並べた順に DI を登録し、後ろに置いたモジュールが前の束縛を差し替える。 案件固有の実装は継承や上書きではなく、後ろにモジュールを足すことで注入する。

CompositeModule([Core(), Authority(), Tenant(), AcmeOverrides()])

適合テスト

自分のアダプタ実装が契約を満たすかを、利用側の CI で検証する。

from ddd4py.common.testing import verify_consumed_notification_store

def test_postgresql_consumed_notification_store(store):
    verify_consumed_notification_store(store)

開発

task init          # 依存インストール
task test          # テスト
task style:check   # ruff / mypy
task style:check:arch   # clean-architecture による自分自身への契約検査

リリース

PyPI への公開は GitHub Release をトリガーに、Trusted Publishing (OIDC) で自動実行される。 API トークンはリポジトリに置かない。

# 1. pyproject.toml の version を上げて main へマージする
# 2. その version と同じタグで Release を作る (v プレフィックス付き)
gh release create v0.1.0 --generate-notes

タグと pyproject.toml の version が食い違うとワークフローは公開前に落ちる。 __version__pyproject.toml から読むため、バージョンを書き換える箇所は pyproject.toml の 1 行だけ。

ライセンス

MIT

Download files

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

Source Distribution

ddd4py-0.2.0.tar.gz (60.2 kB view details)

Uploaded Source

Built Distribution

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

ddd4py-0.2.0-py3-none-any.whl (43.8 kB view details)

Uploaded Python 3

File details

Details for the file ddd4py-0.2.0.tar.gz.

File metadata

  • Download URL: ddd4py-0.2.0.tar.gz
  • Upload date:
  • Size: 60.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for ddd4py-0.2.0.tar.gz
Algorithm Hash digest
SHA256 b6abd8ef4e9de80d432cb0f4c1512a81bb0125fc2664b2219414a2271d896cc4
MD5 32d4721800a137210e65af929f47b9b9
BLAKE2b-256 21225819abae4f0f7a9c0636f9ab25f6d777c81e6af6150fe2a90b596d2956f3

See more details on using hashes here.

Provenance

The following attestation bundles were made for ddd4py-0.2.0.tar.gz:

Publisher: publish.yml on theindiehacker/ddd4py

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

File details

Details for the file ddd4py-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: ddd4py-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 43.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for ddd4py-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5a7ad4ed1449786bdb48c3a6e8e058a03185c03e48e2832bfe8cc296e06ac2ee
MD5 3eff99803483c4a5ac138911047c7cf3
BLAKE2b-256 4ff92a7c43605a724c71176a38908e1e6a3c9b339fc34a04dac35942f4656073

See more details on using hashes here.

Provenance

The following attestation bundles were made for ddd4py-0.2.0-py3-none-any.whl:

Publisher: publish.yml on theindiehacker/ddd4py

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

Release history Release notifications | RSS feed

0.2.1

2 files

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page