Skip to content
CAI
Software that uses CAICheck a score

ShahriyarR/hexagonal-flask-blog-tutorial

50.8

Adequate · 20 September 2026

693

lines of production code

Python

primary language

4

measurements over time

CAI band scale
CAI trend line
CAI lens gauges

What this system is

This system is a blog application built on a Hexagonal Architecture that manages user accounts and blog posts. It provides core capabilities for creating, reading, updating, and deleting posts and users, with data persistence handled through SQLite repositories. The implementation enforces strict data integrity using Pydantic validation and UUID-based identifiers, while leveraging dependency injection to decouple business logic from infrastructure concerns.

How it got here

2022 — Hexagonal architecture overhaul

3 changes.

The project underwent a significant structural reorganization to implement a new Hexagonal Architecture, replacing the legacy Flask-based implementation with a modernized codebase. This period involved removing old domain models and adapters while establishing comprehensive development tooling, including linting, testing, and security auditing via a new Makefile and pyproject.toml configuration.

2023 — Hexagonal architecture implementation

9 changes.

The blog application was restructured to follow Hexagonal Architecture, introducing explicit dependency injection, domain interfaces, and repository patterns. This period focused on decoupling business logic from persistence details by implementing strict service and unit-of-work contracts, migrating identifiers to UUIDs, and adding Pydantic-based validation for domain models.

Features

Implement database repositories for posts and users

Added concrete repository implementations for the blog's post and user domains. The new PostRepository handles CRUD operations (create, read, update, delete) and list retrieval, including joining with user data for author details. The UserRepository manages user creation, lookup by username or UUID, and listing all users, while also integrating password hashing via Werkzeug for secure storage. Both repositories wrap database operations in specific exception types to handle potential database errors gracefully.

src/blog/adapters/repositories · high confidence

Introduction of domain repository interfaces and exception types

The repository layer now exposes abstract interfaces for persisting blog posts and users, defining standard operations such as adding, retrieving by UUID or username, fetching all records, updating, deleting, and executing raw queries. To support error handling within these operations, specific exception classes for database operations on posts and users have been added to the domain layer.

src/blog/domain/ports/repositories · high confidence

Removals

Removal of legacy Hexagonal Architecture implementation

The application's previous Hexagonal Architecture implementation has been completely removed. This change deletes the Flask-based HTTP blueprints for blog posts, the SQLite database repositories for posts and users, the domain models (Post, User), the input DTOs and service ports, and the dependency injection containers that wired these components together. Users will no longer have access to the blog post creation, update, and deletion features, nor the user management capabilities, that were provided by this specific architectural layer.

src/adapters, src/domain, src/main · high confidence

Behavioural changes

Blog application restructured with Hexagonal Architecture and new dependency injection

The blog application has been rewritten to follow Hexagonal Architecture, introducing a new dependency injection container in the configurator module to manage services and unit of work implementations. Configuration logic has been moved to the configurator package, and the database initialization now uses a simplified init\_app function that registers CLI commands without requiring an explicit container argument.

src/blog/configurator · high confidence

Introduction of Pydantic-based schema validation for Post and User models

The domain model layer now includes explicit Pydantic schemas (in \schemas.py\) and validation utilities (in \validators.py\) to enforce data integrity for Post and User entities. This introduces strict validation rules, such as UUID format checks, title and body length limits, and non-empty field requirements, which are applied via dedicated factory functions (\create\_post\_factory\, \register\_user\_factory\, etc.) before domain objects are instantiated.

src/blog/domain/model · high confidence

Introduction of strict service and unit-of-work interfaces for Post and User domains

New abstract base classes have been added to define the contract for Post and User service operations, as well as Unit of Work patterns for transaction management. The \PostServiceInterface\ and \UserServiceInterface\ now explicitly require a Unit of Work dependency and expose methods for CRUD operations (create, update, delete, get) using specific input/output DTOs. Corresponding \PostUnitOfWorkInterface\ and \UserUnitOfWorkInterface\ classes provide context-manager support for committing or rolling back database transactions, ensuring that repository interactions are properly scoped within a unit of work.

src/blog/domain/ports/services · high confidence

Migrate user and post identifiers from integer IDs to UUIDs

The blog's entrypoint adapters now use UUIDs instead of auto-incrementing integers for identifying users and posts. This change updates the database schema (renaming \id\_\ to \uuid\ and adjusting foreign keys), modifies the Flask blueprints to resolve entities by UUID, and updates the HTML templates to pass and compare UUIDs for actions like editing or deleting posts.

src/blog/adapters/entrypoints · high confidence

Project setup and development tooling overhaul

The repository has been restructured to support a new Hexagonal Architecture implementation, introducing a comprehensive set of development tooling. A new Makefile standardizes common workflows including installation, formatting (isort, black), linting (flake8), type checking (pytype), security auditing (bandit, safety, pip-audit), and testing (pytest). Configuration files for these tools (.flake8, pytype.cfg, .pre-commit-config.yaml) have been added, alongside a MIT license and updated documentation. The .gitignore has been updated to exclude local database files (hexagonal.db, hexagonal\_test.db) and IDE settings (.vscode).

(repo-wide) · high confidence

Refactored service and unit-of-work layers to use explicit dependency injection

The blog application's service layer has been restructured to implement proper ports and adapters. New service classes (PostService, UserService) now explicitly depend on Unit of Work interfaces (PostUnitOfWorkInterface, UserUnitOfWorkInterface) rather than managing database sessions internally. Corresponding Unit of Work implementations (PostUnitOfWork, UserUnitOfWork) have been added to manage database sessions and repositories, ensuring that data operations are wrapped in transactions via context managers. This change improves testability and separation of concerns by decoupling business logic from persistence details.

src/blog/adapters/services · high confidence

Restructured blog domain and adapter package locations

The blog-related code has been reorganized into dedicated sub-packages. The domain logic is now located in src/blog/domain, and the application adapters have been moved from src/adapters/app to src/blog/adapters. This change improves the project's modularity by grouping blog-specific components together.

src/blog/adapters, src/blog/domain · high confidence

Test coverage

Initial test suite for domain models, repositories, and unit of work

Added a comprehensive test suite covering the application's core domain logic and infrastructure abstractions. The tests verify the behavior of Post and User domain models, including creation, equality checks, and validation constraints. It also includes unit tests for the FakePostRepository and FakeUserRepository implementations, ensuring correct add, get, update, and delete operations. Furthermore, integration tests validate the PostUnitOfWork and UserUnitOfWork interfaces, confirming that data persistence and transaction commits work as expected with the fake in-memory storage.

tests · high confidence

Dependencies

Migrate build system to pyproject.toml and update dependencies

The project has replaced the legacy requirements.txt file with a comprehensive pyproject.toml configuration. This change introduces specific version constraints for core dependencies, including dependency-injector (\>= 4.41.0), Flask (\>= 2.3.2), and Pydantic (\>= 2.3.0). It also consolidates development tooling (black, isort, flake8, bandit, etc.) and test dependencies (pytest, pytest-flask, pytest-cov) into optional dependency groups within the new manifest, while switching the build backend to flit\_core.

(dependencies) · high confidence

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

How this codebase got here

Score

  • CAI 50 → 51 (+0.8)
  • Rubric changed (rubric-2026.08.17 → rubric-2026.09.15) — scores are not directly comparable.

Lenses

  • Code Health 100 → 100 (-0.3)
  • Architecture 69 → 66 (-2.9)
  • Maturity 62 → 53 (-9.4)
  • Readiness 26 → 33 (+7.7)
  • Security 100 → 100 (+0.0)
  • Accessibility 78 → 78 (+0.0)

Resolved (7)

  • Coverage not included — suite not readable by the collector
  • Dependency hygiene not measured — no supported dependency manifest was read
  • No exposed public API
  • Test reliability not included
  • The README describes how to install for development but does not explain what flit is or why it is used. (README.md)
  • The README links to The Pattern: Ports and Adapters for Hexagonal Architecture but never explains what each dependency provides (ports/adapters) in the context of this project's layers. (README.md)
  • single-maintainer — knowledge-concentration (bus factor) risk

New (3)

  • Dependency hygiene PARTLY measured — Python dependencies read, no exact pin to grade for currency
  • Documentation: no usage examples (README.md)
  • Duplicated block (6 lines × 2) (src/blog/adapters/repositories/post.py)

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

Survey your own repository

ShahriyarR/hexagonal-flask-blog-tutorial was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.

About this page

  • The score is its most recent published measurement, taken on 20 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
  • Measured at commit 4c1e3a5ccecb8b959d46b93213c615c8e8beba74 — the exact code this score is about.
  • Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
  • Measured by watchdog.canine.dev using codehealth-analyzer preprod-28e75b8e3254.