Skip to content

docs: update coding standards#19

Merged
aseguragonzalez merged 6 commits into
mainfrom
docs/update-coding-standards
May 24, 2026
Merged

docs: update coding standards#19
aseguragonzalez merged 6 commits into
mainfrom
docs/update-coding-standards

Conversation

@aseguragonzalez
Copy link
Copy Markdown
Owner

No description provided.

@aseguragonzalez aseguragonzalez self-assigned this May 22, 2026
Copilot AI review requested due to automatic review settings May 22, 2026 19:17
@codecov
Copy link
Copy Markdown

codecov Bot commented May 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the repository’s coding standards documentation to reflect the current seedwork patterns (DDD/hexagonal layering, command/query handling, domain event coordination, integration events, background tasks, and wiring conventions).

Changes:

  • Reworks the Python baseline and Do/Don’t guidance (ports via Protocol, handler naming, bus stack, deferred domain-event coordination).
  • Expands the document with end-to-end examples for aggregates, domain events, command/query handlers, integration events, background tasks, and repository wiring.
  • Adds/updates naming and folder structure conventions and testing InMemory/spy guidance.
Comments suppressed due to low confidence (2)

docs/coding-standards.md:204

  • In the command example, __post_init__ raises ValueError. RegistryCommandBus only converts DomainError into Result.failed; a ValueError will bubble out as an unhandled exception. Update the example (and guidance) to raise a DomainError subclass for domain/validation failures if you expect failures to be represented as Result.failed.
    def __post_init__(self) -> None:
        if self.initial_balance < 0:
            raise ValueError("initial_balance must be non-negative")

docs/coding-standards.md:200

  • Command (and Query) in this seedwork are defined as @dataclass(frozen=True, kw_only=True), and the examples in docs/examples also use kw_only=True. This example omits kw_only=True, which makes the snippet inconsistent with the established pattern in this repo's examples.
@dataclass(frozen=True)
class OpenAccountCommand(Command):
    account_id: str
    initial_balance: float
    currency: str


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/coding-standards.md Outdated
Comment thread docs/coding-standards.md Outdated
Comment thread docs/coding-standards.md Outdated
Comment thread docs/coding-standards.md Outdated
Comment thread docs/coding-standards.md
Comment thread docs/coding-standards.md
Comment thread docs/coding-standards.md Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (4)

docs/coding-standards.md:99

  • The aggregate example uses @dataclass(..., eq=True) implicitly. In seedwork, Entity/AggregateRoot provide identity-based equality; leaving eq as default will generate a dataclass __eq__ on the subclass and override identity semantics. Set eq=False on aggregate/entity dataclasses (as seedwork does).
```python
from __future__ import annotations
from dataclasses import dataclass, field
from uuid import UUID

docs/coding-standards.md:107

  • BankAccount.open() constructs AccountOpenedPayload(initial_balance=...), but the payload type shown later includes both initial_balance and currency. As written, this example wouldn’t type-check/run; align the factory signature and payload construction with the payload dataclass fields (and remove unused imports like field if not needed).
from seedwork.domain import AggregateRoot

@dataclass(frozen=True, kw_only=True)
class BankAccount(AggregateRoot[BankAccountId]):
    owner_id: BankAccountId
    balance: int

    @classmethod

docs/coding-standards.md:186

  • This section refers to DomainException, but the seedwork defines/catches DomainError (there is no DomainException type). RegistryCommandBus catches DomainError specifically and converts it to Result.failed, so the terminology and behavior described here should be updated accordingly.
- `Protocol` with `Repository[BankAccountId, BankAccount]` as base — structural typing. The method signatures (`find_by_id`, `save`, `delete_by_id`) are inherited from `Repository`; no need to redeclare them.
- `pass` body is intentional: inheriting `Repository[TId, TAgg]` already carries the full contract. Redeclaring methods is redundant and couples the port to the base class's naming.
- Returns domain types, never ORM models.

docs/coding-standards.md:219

  • The command example diverges from seedwork’s conventions and won’t work as shown: (1) seedwork commands are @dataclass(frozen=True, kw_only=True), (2) raising ValueError in __post_init__ won’t be converted into Result.failed (only DomainError is caught), and (3) the BankAccount.open(...) call here doesn’t match the aggregate factory signature shown above (missing required args / differing types).
- Extend `DomainError`, the domain base exception type defined in the seedwork.
- The `RegistryCommandBus` catches `DomainError` and returns it in `Result.failed`.
- Keep error classes thin — message goes in the constructor argument.

---

## Application layer

### Command and handler

```python
from __future__ import annotations
from dataclasses import dataclass
from uuid import UUID
from seedwork.application import Command, CommandHandler
from seedwork.domain import DomainError


class InvalidInitialBalanceError(DomainError):
    pass

Comment thread docs/coding-standards.md
Comment thread docs/coding-standards.md
Comment thread docs/coding-standards.md Outdated
Comment thread docs/coding-standards.md
Comment thread docs/coding-standards.md Outdated
Comment thread docs/coding-standards.md
- Add scope note clarifying these standards apply to consumer projects
- Entity: use Entity[TId] base (frozen, eq=False, kw_only) + validate()
- ValueObject: use ValueObject base + validate() raising DomainError subclasses
- Aggregate: add eq=False, validate(), fix AccountOpenedPayload to include currency
- Domain Events: clarify aggregate_id is a required argument (not automatic)
- Errors: fix DomainException→DomainError; show __init__ with message+code
- Command: add kw_only=True; raise DomainError subclass instead of ValueError
- Query: add Query[AccountView] type param and kw_only=True
- Integration event TYPE: align with example ("bank_account.account_opened")
- Repository impl: rename to SqlAlchemyBankAccountRepository per convention

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@aseguragonzalez aseguragonzalez force-pushed the docs/update-coding-standards branch from 8efde84 to 652da57 Compare May 22, 2026 19:41
- Add intro paragraphs and Key points / Do / Don't structure to all
  domain and application layer sections
- Rename Errors → Domain Errors; Domain Event handler → DomainEventHandler
- Add create() factory pattern for domain events with examples
- Add deletion note to Domain Events section
- Reorder Application layer: Integration Events and Background Tasks
  before Execution context and DomainEventHandler
- Remove File/folder structure section
- Fix Do/Don't overview table: remove stale rows, improve wording
- Update CLAUDE.md: fix layer dependency attribution (Hexagonal
  Architecture, not DDD), correct testing patterns, add create()
  factory design decision

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 24, 2026 18:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (1)

docs/coding-standards.md:322

  • In the command handler example, owner_id is set from command.account_id and wrapped as BankAccountId(...). This is a concrete example of the ID mix-up the earlier NewType guidance claims to prevent; update the example to take a distinct owner_id input (and type) or avoid setting owner_id from the account id.
        account = BankAccount.open(
            id=BankAccountId(command.account_id),
            owner_id=BankAccountId(command.account_id),
            initial_balance=Money(amount=command.initial_balance, currency=command.currency),
        )

Comment thread docs/coding-standards.md Outdated
Comment thread docs/coding-standards.md Outdated
Comment thread docs/coding-standards.md Outdated
Comment thread docs/coding-standards.md
Comment thread docs/coding-standards.md
Comment thread CLAUDE.md
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 24, 2026 18:30
aseguragonzalez and others added 2 commits May 24, 2026 20:31
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (1)

docs/coding-standards.md:222

  • Related to the create() factory guidance: the “Don’t: call the constructor directly… always go through create()” rule conflicts with the repository’s current example bounded context (docs/examples/bank_account), which instantiates events via the constructor, and may confuse readers trying to follow the examples. Either update the examples to use create(), or adjust this section to match the existing constructor-based pattern.

- Use a `create()` classmethod as factory — callers pass plain data, the factory constructs the payload internally. This decouples the aggregate from the payload structure.

#### Don't

- Call the constructor directly from the aggregate — always go through `create()`.

Comment thread docs/coding-standards.md
Comment thread docs/coding-standards.md
Comment thread docs/coding-standards.md
Comment thread docs/coding-standards.md
Comment thread docs/coding-standards.md
Comment thread CLAUDE.md
…ory pattern

- Introduce UserId NewType and add owner_id field to BankAccount aggregate
- Update open() and reconstitute() signatures to require owner_id
- Add owner_id to OpenAccountCommand and wire it through the handler
- Apply create() factory classmethod to all domain event classes
- Fix Entity section in coding-standards.md (owner_id: UserId, not AccountId)
- Update all test call sites across docs/examples and tests/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@aseguragonzalez aseguragonzalez merged commit e5c70d1 into main May 24, 2026
9 checks passed
@aseguragonzalez aseguragonzalez deleted the docs/update-coding-standards branch May 24, 2026 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants