"""Example command: CreateContact using the Command Pattern. This is a reference implementation for new modules. Existing contact_service.py is NOT changed — this is an alternative path. Usage: @router.post("/contacts-v2") async def create_contact_v2( body: CreateContactDTO, db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_permission("contacts:write")), ): ctx = RequestContext( user_id=uuid.UUID(current_user["user_id"]), tenant_id=uuid.UUID(current_user["tenant_id"]), is_system_admin=current_user.get("is_system_admin", False), permissions=set(current_user.get("permissions", [])), ) cmd = CreateContactCommand( firstname=body.firstname, surname=body.surname, email=body.email, ) handler = CreateContactHandler() return await handler.execute(cmd, ctx, db) """ from __future__ import annotations import uuid from dataclasses import dataclass from typing import Any from app.core.commands import CommandHandler, RequestContext, UnitOfWork from app.models.contact import Contact @dataclass class CreateContactCommand: """Command to create a new contact.""" firstname: str surname: str email: str | None = None phone: str | None = None company: str | None = None class CreateContactHandler(CommandHandler[CreateContactCommand, dict[str, Any]]): """Handler for CreateContactCommand. Demonstrates the Command Pattern: 1. Authorization check (ctx.require) 2. Domain operation (create Contact) 3. Outbox event (crm.contact.created.v1) 4. Audit log (contact.created) 5. Single commit via UoW """ async def handle(self, cmd: CreateContactCommand, ctx: RequestContext, uow: UnitOfWork) -> dict[str, Any]: # 1. Authorization ctx.require("contacts:write") # 2. Domain operation contact = Contact( tenant_id=ctx.tenant_id, firstname=cmd.firstname, surname=cmd.surname, email_1=cmd.email, phone_1=cmd.phone, company=cmd.company, owner_id=ctx.user_id, created_by=ctx.user_id, updated_by=ctx.user_id, ) uow.add(contact) # 3. Outbox event (standardized envelope) uow.outbox_add( event_name="crm.contact.created.v1", aggregate_id=contact.id, # Will be set after flush aggregate_type="contact", payload={ "firstname": cmd.firstname, "surname": cmd.surname, "email": cmd.email, }, ) # 4. Audit log uow.audit_record( action="create", entity_id=contact.id, entity_type="contact", changes={"firstname": cmd.firstname, "surname": cmd.surname, "email": cmd.email}, ) # 5. Return dict (will be populated after flush in commit) return { "id": str(contact.id), "firstname": contact.firstname, "surname": contact.surname, "email_1": contact.email_1, }