Files
blog.pyaqa.ru/app/application/use_cases/update_post.py
Sergey Vanyushkin 87b094220d refactor: migrate to DDD architecture with Dishka DI
Complete architectural refactoring from simple MVC to Clean Architecture/DDD pattern:

Domain Layer:

- Add entities (Post, BaseEntity) with business logic

- Add value objects (Title, Content, Slug) with validation

- Add repository interfaces (PostRepository)

- Add domain exceptions

Application Layer:

- Add use cases (CreatePost, GetPost, UpdatePost, DeletePost, ListPosts, PublishPost)

- Add DTOs for data transfer

- Add TransactionManager interface

Infrastructure Layer:

- Add SQLAlchemy models and async database connection

- Add SQLAlchemyPostRepository implementation

- Add Dishka DI container with providers

- Add error handlers and middleware

Presentation Layer:

- Add FastAPI routes with Dishka integration

- Add Pydantic schemas

- Add dependency injection using FromDishka[T]

Other Changes:

- Remove old flat structure (api/, common/, core/, modules/)

- Add hatchling build system for package scripts

- Add blog CLI command

- Update AGENTS.md with new architecture docs

- All 48 tests passing, mypy clean, ruff clean
2026-05-01 20:20:41 +03:00

74 lines
2.2 KiB
Python

"""Update post use case."""
from uuid import UUID
from app.application.dtos.post import PostResponseDTO, UpdatePostDTO
from app.application.interfaces import TransactionManager
from app.domain.entities import Post
from app.domain.exceptions import ForbiddenException, NotFoundException
from app.domain.repositories import PostRepository
from app.domain.value_objects import Content, Title
class UpdatePostUseCase:
"""Use case for updating a blog post."""
def __init__(
self,
post_repo: PostRepository,
tx_manager: TransactionManager,
) -> None:
self._post_repo = post_repo
self._tx_manager = tx_manager
async def execute(
self,
post_id: UUID,
dto: UpdatePostDTO,
current_user_id: str,
) -> PostResponseDTO:
"""Execute the use case."""
post = await self._post_repo.get_by_id(post_id)
if not post:
raise NotFoundException(f"Post with id '{post_id}' not found")
# Check authorization
if post.author_id != current_user_id:
raise ForbiddenException("You can only update your own posts")
# Update fields
if dto.title is not None:
post.update_title(Title(dto.title))
if dto.content is not None:
post.update_content(Content(dto.content))
if dto.tags is not None:
# Replace all tags
for tag in post.tags[:]:
post.remove_tag(tag)
for tag in dto.tags:
post.add_tag(tag)
# Persist changes
await self._post_repo.update(post)
# Commit transaction
await self._tx_manager.commit()
return self._map_to_dto(post)
def _map_to_dto(self, post: Post) -> PostResponseDTO:
"""Map domain entity to response DTO."""
return PostResponseDTO(
id=post.id,
title=post.title.value,
content=post.content.value,
slug=post.slug.value,
author_id=post.author_id,
published=post.published,
tags=post.tags.copy(),
created_at=post.created_at,
updated_at=post.updated_at,
)