This commit is contained in:
e4www
2026-01-30 16:34:56 +03:00
commit 12f2ceaf9b
27 changed files with 1331 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from uuid import UUID
@dataclass(frozen=True)
class UserDTO:
id: UUID
email: str
full_name: str
is_active: bool
created_at: datetime

View File

@@ -0,0 +1,15 @@
from __future__ import annotations
from typing import Protocol, runtime_checkable
from app.application.users.ports.user_repository import UserRepository
@runtime_checkable
class UnitOfWork(Protocol):
users: UserRepository
async def __aenter__(self) -> "UnitOfWork": ...
async def __aexit__(self, exc_type, exc, tb) -> None: ...
async def commit(self) -> None: ...
async def rollback(self) -> None: ...

View File

@@ -0,0 +1,12 @@
from __future__ import annotations
from typing import Protocol
from uuid import UUID
from app.domain.users.entities import User
class UserRepository(Protocol):
async def add(self, user: User) -> None: ...
async def get_by_id(self, user_id: UUID) -> User | None: ...
async def get_by_email(self, email: str) -> User | None: ...

View File

@@ -0,0 +1,55 @@
from __future__ import annotations
from dataclasses import dataclass
from uuid import uuid4
from app.application.users.dto import UserDTO
from app.application.users.ports.unit_of_work import UnitOfWork
from app.domain.users.entities import User
from app.domain.users.services import ensure_unique_email
from app.domain.users.value_objects import Email
@dataclass(frozen=True)
class CreateUserCommand:
email: str
full_name: str
class CreateUserUseCase:
"""
Inbound port (use case).
Outbound ports: UnitOfWork -> UserRepository, and email integration.
"""
def __init__(self, uow: UnitOfWork, welcome_email_sender) -> None:
self._uow = uow
self._welcome_email_sender = welcome_email_sender
async def execute(self, cmd: CreateUserCommand) -> UserDTO:
email_vo = Email(cmd.email)
async with self._uow:
existing = await self._uow.users.get_by_email(str(email_vo))
ensure_unique_email(existing_user=existing)
user = User.register(
user_id=uuid4(), email=email_vo, full_name=cmd.full_name
)
await self._uow.users.add(user)
await self._uow.commit()
for ev in user.pull_events():
from app.domain.users.events import UserRegistered
if isinstance(ev, UserRegistered):
await self._welcome_email_sender.send_welcome(
to_email=ev.email, full_name=cmd.full_name
)
return UserDTO(
id=user.id,
email=str(user.email),
full_name=user.full_name,
is_active=user.is_active,
created_at=user.created_at,
)

View File

@@ -0,0 +1,32 @@
from __future__ import annotations
from dataclasses import dataclass
from uuid import UUID
from app.application.users.dto import UserDTO
from app.application.users.ports.unit_of_work import UnitOfWork
from app.domain.users.exceptions import UserNotFound
@dataclass(frozen=True)
class GetUserQuery:
user_id: UUID
class GetUserUseCase:
def __init__(self, uow: UnitOfWork) -> None:
self._uow = uow
async def execute(self, q: GetUserQuery) -> UserDTO:
async with self._uow:
user = await self._uow.users.get_by_id(q.user_id)
if user is None:
raise UserNotFound("User not found")
return UserDTO(
id=user.id,
email=str(user.email),
full_name=user.full_name,
is_active=user.is_active,
created_at=user.created_at,
)