π Getting Started with NeurogliaΒΆ
Welcome to Neuroglia - a lightweight Python framework for building maintainable microservices using clean architecture principles.
π― What You'll LearnΒΆ
This guide will take you from zero to your first working application in just a few minutes. By the end, you'll understand:
- How to install Neuroglia and create your first project
- The basics of clean architecture and why it matters
- How to build a simple CRUD API using CQRS patterns
- Where to go next for advanced features
New to Clean Architecture?
Don't worry! This guide assumes no prior knowledge. We'll explain concepts as we go.
β‘ Quick InstallationΒΆ
PrerequisitesΒΆ
- Python 3.9 or higher (
python3 --version) - pip (Python package manager)
- Basic familiarity with Python and REST APIs
Install NeurogliaΒΆ
# [Optional] create and activate a virtual environment
mkdir getting-started
python3 -m venv venv
source venv/bin/activate
# Install the framework
pip install neuroglia-python
That's it! Neuroglia is built on FastAPI, so it will install all necessary dependencies automatically.
π Hello World - Your First ApplicationΒΆ
Let's create the simplest possible Neuroglia application to verify everything works.
Step 1: Create a Simple APIΒΆ
Create a file named main.py:
import uvicorn
from neuroglia.hosting.web import WebApplicationBuilder
# Create the application builder
builder = WebApplicationBuilder()
# Build the FastAPI application
app = builder.build()
# Add a simple endpoint
@app.get("/")
async def hello():
return {"message": "Hello from Neuroglia!"}
# Run the application (if executed directly)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Step 2: Run ItΒΆ
Step 3: Test ItΒΆ
Open your browser to http://localhost:8080 or use curl:
π Congratulations! You've just built your first Neuroglia application!
What Just Happened?
WebApplicationBuilder is Neuroglia's main entry point. It sets up FastAPI with sensible defaults and provides hooks for dependency injection, middleware, and more. The .build() method creates the FastAPI app instance, and .run() starts the development server.
ποΈ Understanding Clean ArchitectureΒΆ
Before we build something more complex, let's understand why Neuroglia enforces a specific structure.
The Problem: Spaghetti CodeΒΆ
Traditional applications often mix concerns:
# β Everything in one place - hard to test and maintain
@app.post("/orders")
async def create_order(order_data: dict):
# Database access mixed with business logic
conn = psycopg2.connect("...")
# Business validation mixed with data access
if order_data["total"] < 0:
raise ValueError("Invalid total")
# HTTP concerns mixed with everything else
cursor.execute("INSERT INTO orders...")
return {"id": result}
The Solution: Layered ArchitectureΒΆ
Neuroglia separates your code into clear layers:
π your_project/
βββ api/ # π Handles HTTP requests/responses
βββ application/ # πΌ Orchestrates business operations
βββ domain/ # ποΈ Contains business rules and logic
βββ integration/ # π Talks to databases and external services
Key Benefit: Each layer has one job, making code easier to test, understand, and change.
The Dependency RuleΒΆ
Dependencies only flow inward β toward the domain:
flowchart LR
API[π API] --> APP[πΌ Application]
APP --> DOM[ποΈ Domain]
INT[π Integration] --> DOM
style DOM fill:#e1f5fe,stroke:#0277bd,stroke-width:3px
Why This Matters: Your business logic (Domain) never depends on HTTP, databases, or external services. This means you can:
- Test business logic without a database
- Switch from PostgreSQL to MongoDB without changing business rules
- Change API frameworks without touching core logic
π Building a Real Application: Pizza OrdersΒΆ
Let's build a simple pizza ordering system to see clean architecture in action.
Step 1: Define the DomainΒΆ
The domain represents your business concepts. Create domain/pizza_order.py:
from dataclasses import dataclass
from datetime import datetime
from uuid import uuid4
@dataclass
class PizzaOrder:
"""A pizza order - our core business entity."""
id: str
customer_name: str
pizza_type: str
size: str
created_at: datetime
@staticmethod
def create(customer_name: str, pizza_type: str, size: str):
"""Factory method to create a new order."""
return PizzaOrder(
id=str(uuid4()),
customer_name=customer_name,
pizza_type=pizza_type,
size=size,
created_at=datetime.utcnow()
)
def is_valid(self) -> bool:
"""Business rule: validate order."""
valid_sizes = ["small", "medium", "large"]
return self.size in valid_sizes and len(self.customer_name) > 0
Domain Layer
Notice: No imports from FastAPI, no database code. Just pure Python business logic.
Step 2: Create Commands and Queries (CQRS)ΒΆ
CQRS separates write operations (Commands) from read operations (Queries).
Create application/commands.py:
from dataclasses import dataclass
from neuroglia.mediation import Command
@dataclass
class CreatePizzaOrderCommand(Command[dict]):
"""Command to create a new pizza order."""
customer_name: str
pizza_type: str
size: str
Create application/queries.py:
from dataclasses import dataclass
from neuroglia.mediation import Query
@dataclass
class GetPizzaOrderQuery(Query[dict]):
"""Query to retrieve a pizza order."""
order_id: str
CQRS Pattern
Commands change state (create, update, delete). Queries read state (get, list). Separating them makes code clearer and enables advanced patterns like event sourcing.
Step 3: Implement HandlersΒΆ
Handlers contain the logic to execute commands and queries. Create application/handlers.py:
from neuroglia.mediation import CommandHandler, QueryHandler
from application.commands import CreatePizzaOrderCommand
from application.queries import GetPizzaOrderQuery
from domain.pizza_order import PizzaOrder
# Simple in-memory storage for this example
orders_db = {}
class CreatePizzaOrderHandler(CommandHandler[CreatePizzaOrderCommand, dict]):
"""Handles creating new pizza orders."""
async def handle_async(self, command: CreatePizzaOrderCommand) -> dict:
# Create domain entity
order = PizzaOrder.create(
customer_name=command.customer_name,
pizza_type=command.pizza_type,
size=command.size
)
# Validate business rules
if not order.is_valid():
raise ValueError("Invalid order")
# Store (in real app, this would use a Repository)
orders_db[order.id] = order
# Return result
return {
"id": order.id,
"customer_name": order.customer_name,
"pizza_type": order.pizza_type,
"size": order.size,
"created_at": order.created_at.isoformat()
}
class GetPizzaOrderHandler(QueryHandler[GetPizzaOrderQuery, dict]):
"""Handles retrieving pizza orders."""
async def handle_async(self, query: GetPizzaOrderQuery) -> dict:
order = orders_db.get(query.order_id)
if not order:
return None
return {
"id": order.id,
"customer_name": order.customer_name,
"pizza_type": order.pizza_type,
"size": order.size,
"created_at": order.created_at.isoformat()
}
Step 4: Create API ControllerΒΆ
Now let's expose this via HTTP. Create api/orders_controller.py:
from neuroglia.mvc import ControllerBase
from neuroglia.mediation import Mediator
from application.commands import CreatePizzaOrderCommand
from application.queries import GetPizzaOrderQuery
from classy_fastapi.decorators import get, post
from pydantic import BaseModel
class CreateOrderRequest(BaseModel):
customer_name: str
pizza_type: str
size: str
class OrdersController(ControllerBase):
"""Pizza orders API endpoint."""
@post("/", status_code=201)
async def create_order(self, request: CreateOrderRequest):
"""Create a new pizza order."""
command = CreatePizzaOrderCommand(
customer_name=request.customer_name,
pizza_type=request.pizza_type,
size=request.size
)
result = await self.mediator.execute_async(command)
return result
@get("/{order_id}")
async def get_order(self, order_id: str):
"""Retrieve a pizza order by ID."""
query = GetPizzaOrderQuery(order_id=order_id)
result = await self.mediator.execute_async(query)
return result if result else {"error": "Order not found"}
API Layer
The controller is thin - it just translates HTTP requests to commands/queries and sends them to the Mediator.
Step 5: Wire It All TogetherΒΆ
Update your main.py:
from neuroglia.hosting.web import WebApplicationBuilder
from application.handlers import CreatePizzaOrderHandler, GetPizzaOrderHandler
from api.orders_controller import OrdersController
# Create application builder
builder = WebApplicationBuilder()
# Configure core services
Mediator.configure(builder, ["application.handlers"])
Mapper.configure(builder, ["application.dtos", "api.dtos", "domain.entities"])
# Build and configure app
app = builder.build()
app.use_controllers()
if __name__ == "__main__":
app.run()
Step 6: Test Your APIΒΆ
Run the application:
Create an order:
curl -X POST http://localhost:8080/orders \
-H "Content-Type: application/json" \
-d '{"customer_name":"John","pizza_type":"Margherita","size":"large"}'
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"customer_name": "John",
"pizza_type": "Margherita",
"size": "large",
"created_at": "2025-10-25T10:30:00"
}
Get the order:
π What You Just BuiltΒΆ
You've created a clean architecture application with:
- β
Domain Layer:
PizzaOrderentity with business rules - β Application Layer: Commands, Queries, and Handlers
- β API Layer: REST controller using FastAPI
- β CQRS Pattern: Separate write and read operations
- β Dependency Injection: Automatic service resolution
- β Mediator Pattern: Decoupled command/query execution
π What's Next?ΒΆ
For BeginnersΒΆ
- 3-Minute Bootstrap - See more setup options
- Core Concepts - Understand Clean Architecture, DDD, CQRS (coming soon)
- Complete Tutorial - Build full Mario's Pizzeria app (coming soon)
Learn Framework FeaturesΒΆ
- Dependency Injection - Service lifetime and registration
- CQRS & Mediation - Advanced command/query patterns
- MVC Controllers - REST API development
- Data Access - Repository pattern and persistence
- Event Sourcing - Event-driven architecture
Explore Advanced TopicsΒΆ
- Mario's Pizzeria Tutorial - Complete production app
- Architecture Patterns - Deep dive into patterns
- Sample Applications - More real-world examples
π TroubleshootingΒΆ
Common IssuesΒΆ
Q: Import errors when running the application
A: Make sure Neuroglia is installed: pip install neuroglia
Q: Application won't start
A: Port 8080 is taken. Change the port: app.run(port=8081)
Q: Mediator can't find handlers
A: Ensure you're using Mediator.configure(builder, ["application.handlers"]) to auto-discover handlers in the specified modules.
Q: Module import errors in project
A: Add your project root to PYTHONPATH: export PYTHONPATH=. or run with python -m main
Getting HelpΒΆ
- Documentation: Explore features and patterns
- Examples: Check sample applications
- Issues: Report bugs on GitHub
π‘ Key TakeawaysΒΆ
- Clean Architecture separates concerns into layers with clear dependencies
- CQRS separates writes (Commands) from reads (Queries)
- Mediator decouples controllers from handlers
- Domain Layer contains pure business logic with no external dependencies
- Controllers are thin - they delegate to the application layer
You're Ready!
You now understand the fundamentals of Neuroglia. Ready to explore more? Check out these complete sample applications!
π― Explore Sample ApplicationsΒΆ
Learn from complete, production-ready examples that demonstrate different architectural patterns:
π¦ OpenBank - Event Sourcing & CQRSΒΆ
Perfect for: Financial systems, audit-critical applications, complex domain logic
A complete banking system demonstrating:
- β Event sourcing with KurrentDB (EventStoreDB)
- β Complete CQRS separation (write/read models)
- β Domain-driven design with rich aggregates
- β Read model reconciliation and eventual consistency
- β Snapshot strategy for performance optimization
- β Comprehensive domain events and integration events
When to use this pattern:
- Applications requiring complete audit trails
- Financial transactions and banking systems
- Systems needing time-travel debugging
- Complex business rules with event replay
Explore OpenBank Documentation β
π¨ Simple UI - SubApp Pattern with JWT AuthΒΆ
Perfect for: Internal dashboards, admin tools, task management systems
A modern SPA demonstrating:
- β FastAPI SubApp mounting (UI + API separation)
- β Stateless JWT authentication
- β Role-based access control (RBAC) at application layer
- β Bootstrap 5 frontend with Parcel bundler
- β Clean separation of concerns
When to use this pattern:
- Internal business applications
- Admin dashboards and management tools
- Applications requiring different auth for UI vs API
- Projects needing role-based permissions
# Quick start Simple UI
cd samples/simple-ui
poetry run python main.py
# Visit http://localhost:8000
Explore Simple UI Documentation β
π Mario's Pizzeria - Complete TutorialΒΆ
Perfect for: Learning all framework patterns, e-commerce systems
A comprehensive e-commerce platform featuring:
- β 9-part tutorial series from setup to deployment
- β Order management and kitchen workflows
- β Real-time event-driven processes
- β Keycloak authentication integration
- β MongoDB persistence with domain events
- β Complete observability setup
When to use this pattern:
- Learning the complete Neuroglia framework
- Building order processing systems
- Event-driven workflows
- Standard CRUD with business logic
πΊοΈ Learning PathsΒΆ
Path 1: Quick Learner (1-2 hours)ΒΆ
- β Complete this Getting Started guide
- π Review Simple UI Sample for authentication patterns
- ποΈ Build a small CRUD app using what you learned
Path 2: Comprehensive Learner (1-2 days)ΒΆ
- β Complete this Getting Started guide
- π Work through Mario's Pizzeria Tutorial (9 parts)
- π Study Core Concepts
- π¦ Explore OpenBank for advanced patterns
- π¨ Review Simple UI for authentication
Path 3: Deep Dive (1 week)ΒΆ
- β Complete Path 2
- π Read all Architecture Patterns documentation
- π§ Study RBAC & Authorization Guide
- ποΈ Build a production application using learned patterns
- π Implement observability and monitoring
π Additional ResourcesΒΆ
Framework DocumentationΒΆ
- Feature Documentation - Complete guide to all framework features
- Architecture Patterns - Deep dive into design patterns
- Sample Applications - Real-world example applications
- How-To Guides - Practical implementation guides
External Learning ResourcesΒΆ
Essential ReadingΒΆ
- Clean Architecture by Robert C. Martin
- Domain-Driven Design by Eric Evans
- FastAPI Documentation
- Python Type Hints
Recommended BooksΒΆ
- Clean Code by Robert C. Martin - Writing maintainable code
- Implementing Domain-Driven Design by Vaughn Vernon - Practical DDD
- Enterprise Integration Patterns by Gregor Hohpe - Messaging patterns
- Building Microservices by Sam Newman - Distributed systems