Skip to content

The Three-Layer Architecture

The three-layer architecture is a structural pattern commonly applied to API applications (such as those built with FastAPI) to separate concerns and improve maintainability.

  1. Data Layer:
    • Handles direct database operations.
    • Contains no business logic.
    • Returns raw database results.
  2. Service Layer:
    • Contains the core business logic.
    • Handles exceptions and formatting of business errors.
    • Orchestrates calls to the data layer.
    • Validates business rules.
  3. API Layer (or Web/Presentation Layer):
    • Defines HTTP routes and endpoints.
    • Handles HTTP-specific logic (headers, status codes).
    • Uses the service layer for operations.
    • Maps outputs to response models.

Request Flow

Web Request -> API Layer -> Service Layer -> Data Layer -> Database 
Response    <- API Layer <- Service Layer <- Data Layer <- Database

Benefits

This separation allows for:

  • Better testability (layers can be mocked).
  • Cleaner code organization.
  • Easier maintenance.
  • Clear responsibility boundaries.
  • Reusability of business logic.
  • Strict separation of concerns.

Example Implementation in FastAPI

1. API Layer (web/article.py)

from fastapi import APIRouter, Depends, Response, status
from sqlalchemy.orm import Session
from schemas.article import ArticleCreate, ArticleShow
from service.article import ArticleService

router = APIRouter(prefix="/articles", tags=["Articles"])

@router.post("/", response_model=ArticleShow, status_code=status.HTTP_201_CREATED)
def create_article(article: ArticleCreate, db: Session = Depends(get_db)):
    created_article = ArticleService.create_article(article, db, author_id=1)
    return created_article

2. Service Layer (service/article.py)

from fastapi import HTTPException, status
import data.article as data

class ArticleService:
    @staticmethod
    def create_article(article, db, author_id):
        try:
            return data.db_create_article(article, db, author_id)
        except Exception as e:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Failed to create article: {str(e)}"
            )

3. Data Layer (data/article.py)

from models.article import Article

def db_create_article(article, db, author_id):
    db_article = Article(
        title=article.title,
        slug=article.slug,
        content=article.content,
        author_id=author_id,
        published=False
    )
    db.add(db_article)
    db.commit()
    db.refresh(db_article)
    return db_article