← Назад к вопросам

Какая архитектура на текущем проекте?

1.3 Junior🔥 111 комментариев
#Архитектура систем#Опыт и проекты

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектура текущего проекта

Общее описание

На текущем проекте я работаю с системой анализа и скоринга вопросов собеседования (PrepBro). Это backend система, которая управляет вопросами для различных профессий и методологии проведения интервью.

Архитектурный стиль

Проект использует Microservices + Event-Driven Architecture с Clean Architecture принципами внутри каждого сервиса.

┌─────────────────────────────────────────────────────────┐
│                     Frontend                            │
│            (Next.js React приложение)                  │
└──────────────────┬──────────────────────────────────────┘
                   │
                   ▼
       ┌───────────────────────┐
       │   FastAPI Gateway     │
       │   (REST API, Auth)    │
       └───────────┬───────────┘
                   │
     ┌─────────────┼──────────────┬──────────────┐
     │             │              │              │
     ▼             ▼              ▼              ▼
┌─────────┐  ┌──────────┐  ┌───────────┐  ┌──────────┐
│Questions│  │Answering │  │  Scoring  │  │ Analytics│
│ Service │  │ Service  │  │ Service   │  │ Service  │
└────┬────┘  └────┬─────┘  └─────┬─────┘  └────┬─────┘
     │            │              │             │
     └────────────┼──────────────┼─────────────┘
                  │
                  ▼
     ┌──────────────────────────┐
     │   Message Queue (Kafka)  │
     │   (Event Streaming)      │
     └──────────────┬───────────┘
                    │
        ┌───────────┴───────────┐
        ▼                       ▼
   PostgreSQL             Elasticsearch
   (Primary DB)           (Logging, Search)

Слои архитектуры (Clean Architecture / Onion)

Каждый микросервис имеет четырёхслойную архитектуру:

┌─────────────────────────────────────────┐
│         Presentation Layer              │
│    (REST Controllers, FastAPI Routes)   │
└────────────────┬────────────────────────┘
                 │ ▲
                 ▼ │
┌─────────────────────────────────────────┐
│      Application Layer (Use Cases)      │
│   (Business Logic Orchestration)        │
└────────────────┬────────────────────────┘
                 │ ▲
                 ▼ │
┌─────────────────────────────────────────┐
│      Domain Layer (Business Logic)      │
│    (Entities, Value Objects, Rules)     │
└────────────────┬────────────────────────┘
                 │ ▲
                 ▼ │
┌─────────────────────────────────────────┐
│    Infrastructure Layer                 │
│   (DB, API Clients, Message Queues)     │
└─────────────────────────────────────────┘

Микросервисы в проекте

1. Questions Service

Ответственность:

  • Управление вопросами для разных профессий
  • Категоризация вопросов
  • Версионирование вопросов
  • Поиск и фильтрация вопросов

Компоненты:

Domain:
  - Question (сущность)
  - Profession (категория)
  - QuestionRepository (интерфейс)

Application:
  - GetQuestionsUseCase
  - CreateQuestionUseCase
  - UpdateQuestionUseCase
  - DeleteQuestionUseCase

Infrastructure:
  - PostgreSQL Repository Implementation
  - Elasticsearch Index

Presentation:
  - QuestionController (REST endpoints)

Endpoints:

GET /api/v1/questions?profession_id=xxx
GET /api/v1/questions/{id}
POST /api/v1/questions
PUT /api/v1/questions/{id}
DELETE /api/v1/questions/{id}

2. Answering Service

Ответственность:

  • Выдача следующего вопроса для интервью
  • Сохранение ответов пользователей
  • Управление сессией интервью
  • Трекинг прогресса

Компоненты:

Domain:
  - Answer (сущность)
  - InterviewSession
  - AnswerRepository
  - SessionRepository

Application:
  - GetNextQuestionUseCase
  - SubmitAnswerUseCase
  - GetProgressUseCase
  - StartSessionUseCase

Infrastructure:
  - PostgreSQL
  - Redis (для session state)
  - Kafka Producer (event publishing)

Key Flow:

1. Agent запрашивает next question
   GET /api/v1/agent/answering/next?profession_id=xxx

2. Service:
   - Находит текущую сессию
   - Получает следующий невыполненный вопрос
   - Возвращает JSON с question_id и текстом

3. Agent отправляет ответ
   POST /api/v1/agent/answering/{question_id}
   {"content": "Мой ответ..."}

4. Service:
   - Сохраняет ответ в БД
   - Публикует событие 'answer_submitted' в Kafka
   - Возвращает {"ok": true}

3. Scoring Service

Ответственность:

  • Оценивание качества ответов (пока не используется в моей роли, но есть в архитектуре)
  • Расчёт скора кандидата
  • Аналитика ответов

Компоненты:

Domain:
  - Score (сущность)
  - ScoringCriteria
  - ScoringRepository

Application:
  - ScoreAnswerUseCase
  - GetCandidateScoreUseCase

Infrastructure:
  - PostgreSQL
  - Kafka Consumer (listen to answer_submitted events)
  - ML Model Integration (если нужна автоматическая оценка)

4. Analytics Service

Ответственность:

  • Сбор и анализ данных об интервью
  • Отчёты по вопросам и ответам
  • Метрики качества

Компоненты:

Infrastructure:
  - Elasticsearch (event log)
  - PostgreSQL (aggregated data)
  - Kafka Consumer (event streaming)

Технологический стек

Backend:

  • Language: Python 3.11+
  • Framework: FastAPI (async, high-performance REST API)
  • ORM: SQLAlchemy (для типизации и migrations)
  • Database: PostgreSQL (primary)
  • Message Queue: Kafka (event streaming)
  • Caching: Redis (session state)
  • Search: Elasticsearch (optional, для поиска вопросов)
  • Testing: Pytest (unit, integration tests)
  • API Spec: OpenAPI / Swagger

Infrastructure:

  • Container: Docker
  • Orchestration: Kubernetes (или докер-композ локально)
  • CI/CD: GitHub Actions
  • Monitoring: Prometheus + Grafana (или DataDog)
  • Logging: ELK Stack (Elasticsearch + Logstash + Kibana)

Frontend:

  • Framework: Next.js 14 (React 19)
  • Language: TypeScript
  • Styling: Tailwind CSS
  • Testing: Vitest + Playwright
  • Deployment: Vercel

Communication Patterns

Synchronous (REST API)

Client → Frontend → FastAPI Gateway → Answering Service
                                    ↓
                        Answer saved in DB
                                    ↓
                            Response to Client

Asynchronous (Kafka Events)

Answering Service (Producer)
      ↓
[Kafka: answer_submitted]
      ↓
Scoring Service (Consumer) → Score answer
Analytics Service (Consumer) → Log event
Notification Service (Consumer) → Send notification

Требования к данным

Questions:
  - id (UUID)
  - profession_id (UUID, FK)
  - title (VARCHAR)
  - content (TEXT)
  - version (INT)
  - created_at (TIMESTAMP TZ)
  - updated_at (TIMESTAMP TZ)

Answers:
  - id (UUID)
  - session_id (UUID, FK)
  - question_id (UUID, FK)
  - content (TEXT)
  - created_at (TIMESTAMP TZ)
  - scored (BOOLEAN)
  - score (INT, nullable)

Sessions:
  - id (UUID)
  - profession_id (UUID, FK)
  - user_id (UUID, FK)
  - started_at (TIMESTAMP TZ)
  - completed_at (TIMESTAMP TZ, nullable)
  - status (ENUM: 'active', 'completed', 'abandoned')

Deployment Architecture

┌────────────────────────────────────┐
│   Load Balancer (nginx / AWS ALB)  │
└─────────────────┬──────────────────┘
                  │
        ┌─────────┴─────────┐
        ▼                   ▼
┌──────────────┐    ┌──────────────┐
│  Pod 1       │    │  Pod 2       │
│ (FastAPI)    │    │ (FastAPI)    │
│ Replicas: 3  │    │ (Auto-scale) │
└──────┬───────┘    └──────┬───────┘
       │                   │
       └───────────┬───────┘
                   ▼
       ┌──────────────────────┐
       │  PostgreSQL Primary  │
       │  (Replicated)        │
       └──────────────────────┘

Key Architectural Decisions

1. Microservices вместо Monolith

Плюсы:

  • Независимое развертывание (можно обновить Scoring, не трогая Questions)
  • Масштабирование по сервисам (если Answering нагружен, масштабируем только его)
  • Разные команды на разные сервисы

Как это выглядит в коде:

# answering_service/application/use_cases/submit_answer.py
class SubmitAnswerUseCase:
    def __init__(self, answer_repo: AnswerRepository, event_bus: EventBus):
        self.answer_repo = answer_repo
        self.event_bus = event_bus
    
    async def execute(self, cmd: SubmitAnswerCommand) -> None:
        answer = Answer.create(cmd)
        await self.answer_repo.save(answer)
        # Публикуем событие для других сервисов
        await self.event_bus.publish(
            'answer_submitted',
            {'question_id': cmd.question_id, 'content': cmd.content}
        )

2. Event-Driven Architecture для асинхронной обработки

Зачем:

  • Scoring может обрабатывать ответы асинхронно (не блокирует пользователя)
  • Analytics собирает события для отчётов
  • Система более resilient (если Scoring упадёт, Answering работает)

3. Clean Architecture для тестируемости

Каждый слой имеет интерфейсы:

# Domain (бизнес-логика)
class AnswerRepository(ABC):
    @abstractmethod
    async def save(self, answer: Answer) -> None: ...

# Infrastructure (реализация)
class PostgreSQLAnswerRepository(AnswerRepository):
    async def save(self, answer: Answer) -> None:
        # INSERT в БД

# Для тестов (mock)
class InMemoryAnswerRepository(AnswerRepository):
    async def save(self, answer: Answer) -> None:
        # Сохраняем в памяти

4. Immutable Domain Objects

@dataclass(frozen=True)  # immutable
class Answer:
    id: UUID
    session_id: UUID
    question_id: UUID
    content: str
    created_at: datetime
    
    @staticmethod
    def create(session_id: UUID, question_id: UUID, content: str) -> 'Answer':
        return Answer(
            id=uuid4(),
            session_id=session_id,
            question_id=question_id,
            content=content,
            created_at=datetime.now(UTC)
        )

Мониторинг и Observability

# Каждый endpoint логирует
@app.post("/api/v1/agent/answering/{question_id}")
async def submit_answer(question_id: UUID, cmd: SubmitAnswerRequest):
    logger.info(
        "answer_submitted",
        extra={
            "question_id": str(question_id),
            "session_id": str(cmd.session_id),
            "content_length": len(cmd.content),
            "timestamp": datetime.now(UTC).isoformat()
        }
    )
    # ...

Эта архитектура обеспечивает:

  1. Масштабируемость: каждый сервис масштабируется независимо
  2. Надёжность: отказ одного сервиса не влияет на другие
  3. Гибкость: легко добавить новый сервис (например, Notification Service)
  4. Тестируемость: Clean Architecture позволяет тестировать бизнес-логику независимо
  5. Maintainability: код организован по доменам, просто найти нужное