docs: add AI code generation requirements and comprehensive Google-style docstrings

- Add AI code generation requirements to AGENTS.md
- Add module-level docstrings to all 46 Python modules
- Add detailed Google-style docstrings to all classes and functions
- Remove all inline comments following self-documenting code principle
- Include Args, Returns, Raises sections in function docstrings
- Add Attributes and Examples sections to class docstrings
This commit is contained in:
2026-05-02 13:15:21 +03:00
parent 6a528bcbb9
commit ca4e8877a5
52 changed files with 2043 additions and 304 deletions

View File

@@ -1,4 +1,8 @@
"""Application layer exports."""
"""Application layer exports.
This module re-exports all application layer components including
DTOs, interfaces, and use cases for convenient importing.
"""
from app.application.dtos import CreatePostDTO, PostResponseDTO, UpdatePostDTO
from app.application.interfaces import TransactionManager
@@ -12,13 +16,10 @@ from app.application.use_cases import (
)
__all__ = [
# DTOs
"CreatePostDTO",
"UpdatePostDTO",
"PostResponseDTO",
# Interfaces
"TransactionManager",
# Use Cases
"CreatePostUseCase",
"GetPostUseCase",
"UpdatePostUseCase",

View File

@@ -1,4 +1,8 @@
"""Application DTOs."""
"""Application DTOs.
This module re-exports all Data Transfer Objects used in the
application layer for data communication.
"""
from app.application.dtos.post import CreatePostDTO, PostResponseDTO, UpdatePostDTO

View File

@@ -1,4 +1,9 @@
"""DTOs for post use cases."""
"""DTOs for post use cases.
This module defines Data Transfer Objects used for communication between
application layer use cases and presentation layer. DTOs are immutable
dataclasses that carry data across process boundaries.
"""
from dataclasses import dataclass
from datetime import datetime
@@ -7,7 +12,25 @@ from uuid import UUID
@dataclass(frozen=True)
class CreatePostDTO:
"""DTO for creating a post."""
"""DTO for creating a post.
Carries post creation data from API to use case.
Contains all required fields for post creation.
Attributes:
title: Post title string.
content: Post content string.
author_id: Identifier of the post author.
tags: Optional list of tags for categorization.
Example:
>>> dto = CreatePostDTO(
... title="My Post",
... content="Content here...",
... author_id="user-123",
... tags=["python"]
... )
"""
title: str
content: str
@@ -17,7 +40,19 @@ class CreatePostDTO:
@dataclass(frozen=True)
class UpdatePostDTO:
"""DTO for updating a post."""
"""DTO for updating a post.
Carries optional post update data. All fields are optional
allowing partial updates.
Attributes:
title: Optional new title.
content: Optional new content.
tags: Optional new tags list.
Example:
>>> dto = UpdatePostDTO(title="Updated Title")
"""
title: str | None = None
content: str | None = None
@@ -26,7 +61,35 @@ class UpdatePostDTO:
@dataclass(frozen=True)
class PostResponseDTO:
"""DTO for post response."""
"""DTO for post response.
Carries complete post data for API responses.
Includes all post attributes and metadata.
Attributes:
id: Unique post identifier.
title: Post title.
content: Post content.
slug: URL-friendly slug.
author_id: Author identifier.
published: Publication status.
tags: List of tags.
created_at: Creation timestamp.
updated_at: Last update timestamp.
Example:
>>> dto = PostResponseDTO(
... id=uuid,
... title="Post",
... content="...",
... slug="post",
... author_id="user-123",
... published=True,
... tags=[],
... created_at=datetime.now(),
... updated_at=datetime.now()
... )
"""
id: UUID
title: str

View File

@@ -1,4 +1,8 @@
"""Application interfaces."""
"""Application interfaces.
This module re-exports all abstract interfaces used in the
application layer for dependency inversion.
"""
from app.application.interfaces.transaction_manager import TransactionManager

View File

@@ -1,17 +1,38 @@
"""Transaction Manager interface for managing database transactions."""
"""Transaction Manager interface for managing database transactions.
This module defines the abstract interface for transaction management.
Implementations control transaction boundaries for use cases.
"""
from abc import ABC, abstractmethod
class TransactionManager(ABC):
"""Abstract Transaction Manager for controlling transaction boundaries."""
"""Abstract Transaction Manager for controlling transaction boundaries.
Provides interface for committing or rolling back database transactions.
Used by use cases to manage atomic operations.
Example:
>>> async with transaction_manager:
... await repository.add(entity)
... await transaction_manager.commit()
"""
@abstractmethod
async def commit(self) -> None:
"""Commit the current transaction."""
"""Commit the current transaction.
Persists all pending changes to the database.
Should be called after all operations succeed.
"""
...
@abstractmethod
async def rollback(self) -> None:
"""Rollback the current transaction."""
"""Rollback the current transaction.
Discards all pending changes.
Should be called when an error occurs.
"""
...

View File

@@ -1,4 +1,8 @@
"""Use cases."""
"""Use cases.
This module re-exports all application use cases that implement
business logic operations for the blog API.
"""
from app.application.use_cases.create_post import CreatePostUseCase
from app.application.use_cases.delete_post import DeletePostUseCase

View File

@@ -1,4 +1,8 @@
"""Create post use case."""
"""Create post use case.
This module implements the use case for creating new blog posts.
Handles slug generation, duplicate checking, and entity persistence.
"""
from app.application.dtos.post import CreatePostDTO, PostResponseDTO
from app.application.interfaces import TransactionManager
@@ -8,28 +12,57 @@ from app.domain.repositories import PostRepository
class CreatePostUseCase:
"""Use case for creating a new blog post."""
"""Use case for creating a new blog post.
Encapsulates the business logic for creating posts including
slug generation from title and duplicate slug detection.
Attributes:
_post_repo: Repository for post data access.
_tx_manager: Transaction manager for commit control.
Example:
>>> use_case = CreatePostUseCase(post_repo, tx_manager)
>>> result = await use_case.execute(dto)
"""
def __init__(
self,
post_repo: PostRepository,
tx_manager: TransactionManager,
) -> None:
"""Initialize use case with dependencies.
Args:
post_repo: Repository for post operations.
tx_manager: Transaction manager instance.
"""
self._post_repo = post_repo
self._tx_manager = tx_manager
async def execute(self, dto: CreatePostDTO) -> PostResponseDTO:
"""Execute the use case."""
# Generate slug from title
"""Execute the use case to create a new post.
Args:
dto: Data transfer object containing post creation data
including title, content, author ID, and optional tags.
Returns:
PostResponseDTO with created post data including generated ID and slug.
Raises:
AlreadyExistsException: If a post with the same slug exists.
Note:
Slug is automatically generated from the title.
"""
from app.domain.value_objects import Slug
slug = Slug.from_title(dto.title)
# Check if slug already exists
if await self._post_repo.slug_exists(slug.value):
raise AlreadyExistsException(f"Post with slug '{slug.value}' already exists")
# Create domain entity
post = Post.create(
title_str=dto.title,
content_str=dto.content,
@@ -37,16 +70,20 @@ class CreatePostUseCase:
tags=dto.tags or [],
)
# Persist entity
await self._post_repo.add(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."""
"""Map domain entity to response DTO.
Args:
post: Domain post entity.
Returns:
PostResponseDTO with all post attributes.
"""
return PostResponseDTO(
id=post.id,
title=post.title.value,

View File

@@ -1,4 +1,8 @@
"""Delete post use case."""
"""Delete post use case.
This module implements the use case for deleting blog posts.
Includes authorization checks to ensure users can only delete their own posts.
"""
from uuid import UUID
@@ -8,28 +12,51 @@ from app.domain.repositories import PostRepository
class DeletePostUseCase:
"""Use case for deleting a blog post."""
"""Use case for deleting a blog post.
Handles post deletion with authorization checks.
Users can only delete posts they authored.
Attributes:
_post_repo: Repository for post data access.
_tx_manager: Transaction manager for commit control.
Example:
>>> use_case = DeletePostUseCase(post_repo, tx_manager)
>>> await use_case.execute(post_id, user_id)
"""
def __init__(
self,
post_repo: PostRepository,
tx_manager: TransactionManager,
) -> None:
"""Initialize use case with dependencies.
Args:
post_repo: Repository for post operations.
tx_manager: Transaction manager instance.
"""
self._post_repo = post_repo
self._tx_manager = tx_manager
async def execute(self, post_id: UUID, current_user_id: str) -> None:
"""Execute the use case."""
"""Execute the use case to delete a post.
Args:
post_id: Unique identifier of the post to delete.
current_user_id: ID of the user requesting deletion.
Raises:
NotFoundException: If post with given ID does not exist.
ForbiddenException: If user is not the post author.
"""
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 delete your own posts")
# Delete the post
await self._post_repo.delete(post_id)
# Commit transaction
await self._tx_manager.commit()

View File

@@ -1,4 +1,8 @@
"""Get post use case."""
"""Get post use case.
This module implements the use case for retrieving blog posts.
Supports lookup by both ID and slug.
"""
from uuid import UUID
@@ -10,32 +14,78 @@ from app.domain.repositories import PostRepository
class GetPostUseCase:
"""Use case for retrieving a post by ID or slug."""
"""Use case for retrieving a post by ID or slug.
Provides methods to fetch posts using different identifiers.
Handles not-found scenarios with appropriate exceptions.
Attributes:
_post_repo: Repository for post data access.
_tx_manager: Transaction manager for transaction control.
Example:
>>> use_case = GetPostUseCase(post_repo, tx_manager)
>>> post = await use_case.by_id(post_id)
>>> post = await use_case.by_slug("my-post")
"""
def __init__(
self,
post_repo: PostRepository,
tx_manager: TransactionManager,
) -> None:
"""Initialize use case with dependencies.
Args:
post_repo: Repository for post operations.
tx_manager: Transaction manager instance.
"""
self._post_repo = post_repo
self._tx_manager = tx_manager
async def by_id(self, post_id: UUID) -> PostResponseDTO:
"""Get post by ID."""
"""Get post by ID.
Args:
post_id: Unique identifier of the post.
Returns:
PostResponseDTO with complete post data.
Raises:
NotFoundException: If post with given ID does not exist.
"""
post = await self._post_repo.get_by_id(post_id)
if not post:
raise NotFoundException(f"Post with id '{post_id}' not found")
return self._map_to_dto(post)
async def by_slug(self, slug: str) -> PostResponseDTO:
"""Get post by slug."""
"""Get post by slug.
Args:
slug: URL-friendly slug identifier.
Returns:
PostResponseDTO with complete post data.
Raises:
NotFoundException: If post with given slug does not exist.
"""
post = await self._post_repo.get_by_slug(slug)
if not post:
raise NotFoundException(f"Post with slug '{slug}' not found")
return self._map_to_dto(post)
def _map_to_dto(self, post: Post) -> PostResponseDTO:
"""Map domain entity to response DTO."""
"""Map domain entity to response DTO.
Args:
post: Domain post entity.
Returns:
PostResponseDTO with all post attributes.
"""
return PostResponseDTO(
id=post.id,
title=post.title.value,

View File

@@ -1,4 +1,8 @@
"""List posts use case."""
"""List posts use case.
This module implements the use case for listing blog posts.
Provides multiple query methods including filtering by author, tag, and search.
"""
from app.application.dtos.post import PostResponseDTO
from app.application.interfaces import TransactionManager
@@ -7,18 +11,40 @@ from app.domain.repositories import PostRepository
class ListPostsUseCase:
"""Use case for listing blog posts with filtering."""
"""Use case for listing blog posts with filtering.
Provides various methods to query posts with different criteria
including pagination support for large result sets.
Attributes:
_post_repo: Repository for post data access.
_tx_manager: Transaction manager for transaction control.
Example:
>>> use_case = ListPostsUseCase(post_repo, tx_manager)
>>> posts = await use_case.published_posts(limit=10, offset=0)
"""
def __init__(
self,
post_repo: PostRepository,
tx_manager: TransactionManager,
) -> None:
"""Initialize use case with dependencies.
Args:
post_repo: Repository for post operations.
tx_manager: Transaction manager instance.
"""
self._post_repo = post_repo
self._tx_manager = tx_manager
async def all_posts(self) -> list[PostResponseDTO]:
"""Get all posts."""
"""Get all posts.
Returns:
List of PostResponseDTO for all posts.
"""
posts = await self._post_repo.get_all()
return [self._map_to_dto(post) for post in posts]
@@ -27,7 +53,15 @@ class ListPostsUseCase:
limit: int | None = None,
offset: int | None = None,
) -> list[PostResponseDTO]:
"""Get all published posts."""
"""Get all published posts.
Args:
limit: Maximum number of posts to return.
offset: Number of posts to skip.
Returns:
List of PostResponseDTO for published posts.
"""
posts = await self._post_repo.get_published(limit=limit, offset=offset)
return [self._map_to_dto(post) for post in posts]
@@ -37,7 +71,16 @@ class ListPostsUseCase:
limit: int | None = None,
offset: int | None = None,
) -> list[PostResponseDTO]:
"""Get posts by author."""
"""Get posts by author.
Args:
author_id: Identifier of the author.
limit: Maximum number of posts to return.
offset: Number of posts to skip.
Returns:
List of PostResponseDTO for posts by the author.
"""
posts = await self._post_repo.get_by_author(author_id, limit=limit, offset=offset)
return [self._map_to_dto(post) for post in posts]
@@ -47,7 +90,16 @@ class ListPostsUseCase:
limit: int | None = None,
offset: int | None = None,
) -> list[PostResponseDTO]:
"""Get posts by tag."""
"""Get posts by tag.
Args:
tag: Tag to filter by.
limit: Maximum number of posts to return.
offset: Number of posts to skip.
Returns:
List of PostResponseDTO for posts with the tag.
"""
posts = await self._post_repo.get_by_tag(tag, limit=limit, offset=offset)
return [self._map_to_dto(post) for post in posts]
@@ -57,12 +109,28 @@ class ListPostsUseCase:
limit: int | None = None,
offset: int | None = None,
) -> list[PostResponseDTO]:
"""Search posts."""
"""Search posts.
Args:
query: Search query string.
limit: Maximum number of posts to return.
offset: Number of posts to skip.
Returns:
List of PostResponseDTO for matching posts.
"""
posts = await self._post_repo.search(query, limit=limit, offset=offset)
return [self._map_to_dto(post) for post in posts]
def _map_to_dto(self, post: Post) -> PostResponseDTO:
"""Map domain entity to response DTO."""
"""Map domain entity to response DTO.
Args:
post: Domain post entity.
Returns:
PostResponseDTO with all post attributes.
"""
return PostResponseDTO(
id=post.id,
title=post.title.value,

View File

@@ -1,4 +1,8 @@
"""Publish post use case."""
"""Publish post use case.
This module implements the use case for publishing and unpublishing blog posts.
Includes authorization checks to ensure users can only manage their own posts.
"""
from uuid import UUID
@@ -10,18 +14,48 @@ from app.domain.repositories import PostRepository
class PublishPostUseCase:
"""Use case for publishing/unpublishing a blog post."""
"""Use case for publishing/unpublishing a blog post.
Handles post publication state changes with authorization checks.
Users can only publish or unpublish posts they authored.
Attributes:
_post_repo: Repository for post data access.
_tx_manager: Transaction manager for commit control.
Example:
>>> use_case = PublishPostUseCase(post_repo, tx_manager)
>>> post = await use_case.publish(post_id, user_id)
"""
def __init__(
self,
post_repo: PostRepository,
tx_manager: TransactionManager,
) -> None:
"""Initialize use case with dependencies.
Args:
post_repo: Repository for post operations.
tx_manager: Transaction manager instance.
"""
self._post_repo = post_repo
self._tx_manager = tx_manager
async def publish(self, post_id: UUID, current_user_id: str) -> PostResponseDTO:
"""Publish a post."""
"""Publish a post.
Args:
post_id: Unique identifier of the post.
current_user_id: ID of the user requesting publication.
Returns:
PostResponseDTO with updated post data.
Raises:
NotFoundException: If post with given ID does not exist.
ForbiddenException: If user is not the post author.
"""
post = await self._post_repo.get_by_id(post_id)
if not post:
raise NotFoundException(f"Post with id '{post_id}' not found")
@@ -36,7 +70,19 @@ class PublishPostUseCase:
return self._map_to_dto(post)
async def unpublish(self, post_id: UUID, current_user_id: str) -> PostResponseDTO:
"""Unpublish a post."""
"""Unpublish a post.
Args:
post_id: Unique identifier of the post.
current_user_id: ID of the user requesting unpublish.
Returns:
PostResponseDTO with updated post data.
Raises:
NotFoundException: If post with given ID does not exist.
ForbiddenException: If user is not the post author.
"""
post = await self._post_repo.get_by_id(post_id)
if not post:
raise NotFoundException(f"Post with id '{post_id}' not found")
@@ -51,7 +97,14 @@ class PublishPostUseCase:
return self._map_to_dto(post)
def _map_to_dto(self, post: Post) -> PostResponseDTO:
"""Map domain entity to response DTO."""
"""Map domain entity to response DTO.
Args:
post: Domain post entity.
Returns:
PostResponseDTO with all post attributes.
"""
return PostResponseDTO(
id=post.id,
title=post.title.value,

View File

@@ -1,4 +1,8 @@
"""Update post use case."""
"""Update post use case.
This module implements the use case for updating blog posts.
Supports partial updates and includes authorization checks.
"""
from uuid import UUID
@@ -11,13 +15,33 @@ from app.domain.value_objects import Content, Title
class UpdatePostUseCase:
"""Use case for updating a blog post."""
"""Use case for updating a blog post.
Handles post updates with authorization checks.
Supports partial updates - only provided fields are changed.
Users can only update posts they authored.
Attributes:
_post_repo: Repository for post data access.
_tx_manager: Transaction manager for commit control.
Example:
>>> use_case = UpdatePostUseCase(post_repo, tx_manager)
>>> dto = UpdatePostDTO(title="New Title")
>>> result = await use_case.execute(post_id, dto, user_id)
"""
def __init__(
self,
post_repo: PostRepository,
tx_manager: TransactionManager,
) -> None:
"""Initialize use case with dependencies.
Args:
post_repo: Repository for post operations.
tx_manager: Transaction manager instance.
"""
self._post_repo = post_repo
self._tx_manager = tx_manager
@@ -27,16 +51,27 @@ class UpdatePostUseCase:
dto: UpdatePostDTO,
current_user_id: str,
) -> PostResponseDTO:
"""Execute the use case."""
"""Execute the use case to update a post.
Args:
post_id: Unique identifier of the post to update.
dto: Data transfer object with update data.
current_user_id: ID of the user requesting update.
Returns:
PostResponseDTO with updated post data.
Raises:
NotFoundException: If post with given ID does not exist.
ForbiddenException: If user is not the post author.
"""
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))
@@ -44,22 +79,25 @@ class UpdatePostUseCase:
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."""
"""Map domain entity to response DTO.
Args:
post: Domain post entity.
Returns:
PostResponseDTO with all post attributes.
"""
return PostResponseDTO(
id=post.id,
title=post.title.value,