Thanks to visit codestin.com
Credit goes to github.com

Skip to content

mohanadtoaima/team-operations-service

Repository files navigation

Team Operations Service

CI Coverage Python FastAPI

A backend service demonstrating production-grade system design: clean architecture, domain-driven design, RBAC, event-driven notifications, and explicit workflow state machines — patterns drawn from real-world SaaS products.


Architecture

The system is organized around a strict layered architecture. Each layer has a single responsibility and no layer reaches past its immediate neighbor.

flowchart LR
    Client[Client / Swagger UI]
    API[FastAPI\nAPI Layer]
    Domain[Domain Services\n& Policies]
    DB[(PostgreSQL)]
    Redis[(Redis Queue)]
    Worker[Notification\nWorker]
 
    Client --> API
    API --> Domain
    Domain --> DB
    Domain --> Redis
    Redis --> Worker
Loading

Layer Responsibilities

  • API Layer — HTTP routing, request validation, authentication. Routes are intentionally thin and delegate all logic downward.
  • Domain Services — All business logic and workflow transitions live here, independent of FastAPI.
  • Policies — Authorization rules are extracted into a dedicated TaskPolicy class, keeping them testable and separate from business logic.
  • Infrastructure — Database sessions, JWT auth, and Redis publishing are isolated behind the infrastructure boundary.
  • Worker — A standalone async process consuming the Redis notification queue, decoupled from the main request lifecycle.

Design Decisions

Why Policies Instead of Inline Permission Checks?

Authorization logic lives in TaskPolicy rather than being scattered across routes or services. This keeps:

  • permissions easy to test in isolation
  • business logic focused on workflow, not access control
  • future role expansion contained to a single, predictable place

Why a State Machine for Task Transitions?

Task status changes are controlled through an explicit state machine rather than allowing arbitrary status updates. Invalid transitions are rejected at the domain level. This mirrors how production approval workflows enforce data integrity — preventing states like jumping from draft directly to approved.

Why Redis Instead of Celery?

Redis-backed queue publishing was intentionally chosen as a lightweight first step:

  • simpler infrastructure footprint
  • sufficient to demonstrate async, event-driven architecture
  • a clear and honest upgrade path to Celery for more complex job scheduling

Why Keep Logic in Domain Services Instead of Controllers?

Routes are thin by design. Business rules in the domain layer can be reused, tested independently, and remain decoupled from FastAPI's request/response model. This also makes future transport changes (e.g., adding a CLI or gRPC interface) straightforward.


Features

Task Workflow

Tasks move through a controlled, validated state machine:

Draft → In Progress → Pending Approval → Approved / Rejected → Completed

Valid transitions:

From To
draft in_progress
in_progress pending_approval
pending_approval approved
pending_approval rejected
approved completed

All other transitions are rejected.

Role-Based Access Control

Role Permissions
member Create, start, and submit their own tasks
manager Approve or reject tasks assigned to them
admin All actions

Audit Logging

Every workflow action creates an immutable audit log entry capturing: entity type, entity ID, action, performer, metadata, and timestamp.

Logged events: task_started, task_submitted_for_approval, task_approved, task_rejected, task_completed

Async Notifications

Workflow actions publish events to a Redis queue. A background worker consumes and processes them asynchronously, decoupled from the main API request cycle.


Tech Stack

Layer Technology
API Framework FastAPI
ORM SQLAlchemy 2.0
Database PostgreSQL 17
Migrations Alembic
Async Queue Redis 7
Schema Validation Pydantic
Testing Pytest
Runtime Python 3.13
Containers Docker / Docker Compose

Domain Model

  • User — authenticated actor with an assigned role
  • Team — group of users tasks belong to
  • Task — core entity with lifecycle state
  • Approval — approval request linking a task to an approver
  • AuditLog — immutable record of all domain events

Running Locally

Prerequisites

  • Docker & Docker Compose
  • Python 3.13+

1. Clone the repository

git clone https://github.com/mohanadtoaima/team-operations-service.git
cd team-operations-service

2. Configure environment variables

cp .env.example .env

Edit .env as needed. Defaults work out of the box with Docker Compose.

3. Start all services

docker compose up -d

This starts PostgreSQL, Redis, and the API. The API will be available at http://localhost:8000.

4. Run database migrations

alembic upgrade head

5. Seed initial data

python -m scripts.seed

Seeded accounts:

Role Email Password
Admin [email protected] admin123
Manager [email protected] manager123
Member [email protected] member123

6. Explore the API

Swagger UI: http://localhost:8000/docs

To authenticate in Swagger: use OAuth2 password flow with email as username. Leave client_id and client_secret empty.

7. Run the notification worker (optional, separate terminal)

python -m app.workers.notification_worker

The worker listens on notifications:queue and logs consumed events:

[NOTIFICATION WORKER] type=task_approved recipient=3 task=5 message=Manager User approved task 'Prepare weekly report'.

Example Workflow

1. Login as member      → POST /auth/login
2. Create a task        → POST /tasks
3. Start the task       → POST /tasks/{id}/start
4. Submit for approval  → POST /tasks/{id}/submit-for-approval  { "approver_id": 2 }
5. Login as manager     → POST /auth/login
6. Approve or reject    → POST /tasks/{id}/approve  |  POST /tasks/{id}/reject
7. Login as member      → POST /auth/login
8. Complete the task    → POST /tasks/{id}/complete
9. Inspect audit logs   → GET  /audit-logs

Example API Requests

Create Task

POST /tasks
{
  "title": "Prepare weekly report",
  "description": "Compile weekly project status",
  "team_id": 1,
  "assigned_to": 3,
  "due_date": null
}

Submit For Approval

POST /tasks/{task_id}/submit-for-approval
{
  "approver_id": 2
}

Reject Task

POST /tasks/{task_id}/reject
{
  "comment": "Needs more details in section 2"
}

Running Tests

pytest

The test suite covers:

  • Domain service logic
  • Policy enforcement
  • Authentication
  • End-to-end task workflow (integration tests)

Project Structure

app/
├── api/
│   ├── dependencies/
│   └── routes/
├── domain/
│   ├── exceptions/
│   ├── models/
│   ├── policies/
│   └── services/
├── infrastructure/
│   ├── auth/
│   ├── db/
│   └── notifications/
├── schemas/
├── workers/
└── main.py
 
scripts/
├── seed.py
├── seed_users.py
└── seed_teams.py
 
tests/
├── domain/
└── integration/

Future Improvements

  • Multi-step approval chains (multiple approvers per task)
  • WebSocket or email notifications via a real notification provider
  • Pagination and filtering on task listing endpoints

License

This project is for portfolio and learning purposes.

About

A backend service built to demonstrate production-grade system design: clean architecture, domain-driven design, RBAC, event-driven notifications, and structured workflow state machines — patterns drawn from real SaaS products.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors