Skip to content

Architecture

santiago edited this page Jan 29, 2026 · 1 revision

Architecture

This framework is designed to be flexible and support various architectural patterns.

Overview

azfunc-boot provides a foundation for building well-structured Azure Functions applications without enforcing a rigid architecture. You can organize your code however you prefer.

Core Components

Dependency Container

The heart of the framework, managing service registration and resolution:

  • Service Registration: Register services with different lifetimes
  • Automatic Resolution: Resolve dependencies using type annotations
  • Scope Management: Create and manage scoped service instances

Controllers

Organize Azure Functions triggers using a class-based approach:

  • Route Organization: Group related routes together
  • Automatic Discovery: Controllers are automatically discovered and registered
  • Scope Management: Automatic scope creation per request/trigger

Service Registries

Centralized service registration:

  • Automatic Discovery: Registries are automatically discovered
  • Organized Registration: Group related service registrations
  • Flexible Registration: Support for simple and lambda-based registration

Architectural Patterns

The framework supports (but doesn't enforce) various architectural patterns:

Controllers (Adapters)

Handle HTTP requests and Azure Functions triggers:

class UserController(BaseController):
    # Handles HTTP requests, triggers, etc.
    pass

Services (Application Layer)

Business logic and orchestration (optional):

class UserService:
    def __init__(self, repository: IUserRepository):
        self.repository = repository
    
    async def create_user(self, data: dict):
        # Business logic here
        pass

Repositories (Ports/Adapters)

Data access abstraction (optional):

class UserRepository:
    def __init__(self, db_context: IDatabaseContext):
        self.db = db_context
    
    async def find_by_id(self, user_id: str):
        # Data access logic
        pass

Domain Models (Core)

Business entities (optional):

class User:
    def __init__(self, id: str, name: str, email: str):
        self.id = id
        self.name = name
        self.email = email

Flexible Structure

Important: The framework doesn't enforce any specific structure. You can:

  • Use or skip service layers
  • Use or skip repository patterns
  • Organize code in any way you prefer
  • Mix and match patterns as needed

The only requirements are:

  • Controllers must extend BaseController
  • Service registries must extend BaseServiceRegistry

Example Project Structure

Here's one possible structure (you can organize differently):

project/
├── controllers/
│   ├── __init__.py
│   ├── user_controller.py
│   └── product_controller.py
├── services/
│   ├── __init__.py
│   ├── user_service.py
│   └── product_service.py
├── repositories/
│   ├── __init__.py
│   ├── user_repository.py
│   └── product_repository.py
├── registries/
│   ├── __init__.py
│   ├── services_registry.py
│   └── repositories_registry.py
├── models/
│   ├── __init__.py
│   ├── user.py
│   └── product.py
└── function_app.py

But you could also organize as:

project/
├── features/
│   ├── users/
│   │   ├── controller.py
│   │   ├── service.py
│   │   └── repository.py
│   └── products/
│       ├── controller.py
│       ├── service.py
│       └── repository.py
├── registries/
│   └── services_registry.py
└── function_app.py

Or any other structure that works for your project!

Dependency Flow

Controller
    ↓ (depends on)
Service (optional)
    ↓ (depends on)
Repository (optional)
    ↓ (depends on)
Database/External Service

Automatic Discovery

The framework automatically discovers:

  1. Controllers: All classes extending BaseController in the controllers package
  2. Service Registries: All classes extending BaseServiceRegistry in the registries package

This happens during create_app():

app, container = create_app(
    controllers_package="controllers",
    registries_package="registries"
)

Scope Lifecycle

  1. Request/Trigger Starts: A new scope is created
  2. Service Resolution: Services are resolved within the scope
  3. Request Processing: Your code executes
  4. Scope Ends: Scoped services are disposed (if they implement IDisposable)

Benefits

  • Flexibility: Organize code however you prefer
  • Testability: Easy to mock dependencies
  • Maintainability: Clear separation of concerns
  • Scalability: Easy to add new features
  • Type Safety: Type annotations for better IDE support

Related Topics

Clone this wiki locally