Porto Architectural Pattern

repository·master·Indexed 23 days ago

https://github.com/mahmoudz/porto

A software architectural pattern that organizes code into self-contained Containers for business logic and a Ship layer for infrastructure to facilitate scaling from monoliths to microservices. It features two editions: Porto SAP for human-readable, security-critical systems, and Porto AI optimized for autonomous AI agents. The architecture utilizes Actions and Tasks to structure business logic and defines a strict Request Life Cycle from Endpoints through Controllers to the Data Layer.

Tokens
12.6K
Snippets
9
Records
68
Agent score
81%

What's inside porto

  1. What is Porto SAP?

    master

    Porto SAP (Software Architectural Pattern) is a scalable framework of guidelines, principles, and patterns designed for creating maintainable and reusable software. It is specifically optimized for teams who review and extend code themselves, using AI as an assistant rather than the primary author.

    Key characteristics include:

    • Scalability: Allows starting with a clean monolith and transitioning to microservices via a modular structure.
    • AI-Friendly: Adheres to the Single Responsibility Principle, making the codebase easier for AI coding assistants to navigate and contribute to.
    • Foundation: Grounded in Domain Driven Design (DDD), Modular, Micro Kernel, MVC, Layered, and Action Domain Responder (ADR) architectures.
    • Target Use Case: Best suited for medium to large-sized backend web applications.
  2. What is Porto and how does it work?

    master

    Porto is a scalable software architectural pattern designed to prevent code entanglement by organizing code into self-contained, reusable units. It separates business logic from infrastructure to allow seamless scaling from a monolith to microservices.

    There are two editions of Porto:

    • Porto SAP: Designed for human-readable, high-trust, and security-critical systems where humans write and review code with AI assistance.
    • Porto AI (private testing): Optimized for codebases maintained primarily by autonomous AI agents, focusing on fewer files and fewer tokens for faster delivery.
  3. Overview of Porto's optional components

    master

    Porto provides a wide range of optional components that can be integrated to enhance application capabilities, maintainability, and modularity. You should select only the components relevant to your specific use case to avoid unnecessary complexity.

    Key categories of components include:

    • Data & Persistence: Repositories (abstracting persistence), Migrations (schema versioning), Seeders (initial data), Factories (test data generation), Criteria (complex query building), and Value Objects (simple entities).
    • Logic & Execution: Actions (business logic), Tasks (unit of work), Jobs (background tasks), Commands (CLI interactions), and Middlewares (HTTP request/response processing).
    • Communication & Events: Events Publisher (broadcasting events), Event Handlers (responding to events), Data Transfer Objects (carrying data between systems), and Mails (emailing).
    • Structure & Safety: Contracts (defining interfaces), Policies (authorization), Exceptions (error handling), Configs (settings), and Localizations (translations).
    • Testing: Tests (Unit, Integration, Performance, Security, Functional, and Acceptance).
  4. Explore Porto architecture implementations

    master

    Porto is implemented across various languages and frameworks. You can use these existing projects as references or foundations for building Porto-based applications:

    PHP Implementations

    Python Implementations

  5. What is Porto AI?

    master

    Porto AI is a specialized edition of the Porto software architectural pattern designed specifically for codebases maintained and evolved by AI agents rather than humans. While it retains the core Porto concept of self-contained, reusable units with clear boundaries, it reshapes the repository structure to optimize for agent workflows.

    Key characteristics include:

    • Flat, predictable structure: Minimizes folder traversal and deep hierarchies to reduce the number of hops an agent must make to reach code.
    • High density: Logic, types, and configuration for a single unit are co-located in one place rather than being spread across many small files.
    • Machine-readable definitions: Uses structured context and agent skills instead of prose documentation, allowing agents to consume codebase rules directly.
    • Token efficiency: Designed to minimize the surface area an agent needs to load, reducing token costs and round trips.
    • Agent-agnostic: The optimization is implemented at the repository level, making it compatible with any coding assistant (e.g., Claude Code, Codex, GitHub Copilot, Cursor).
  6. Enhance data flow using CQRS

    master

    To optimize performance, scalability, and security, you can implement Command Query Responsibility Segregation (CQRS) within the Data Layer. This pattern separates read and write operations into different models:

    • Commands (Write operations): Handled by Data Services.
    • Queries (Read operations): Managed by Repositories.

    This segregation allows the system to handle high loads and complex data structures more efficiently by decoupling the logic used to change data from the logic used to retrieve it.

  7. Understand the Ship Layer in Porto architecture

    master

    The Ship Layer is the backbone of the Porto architecture, responsible for system organization, decoupling, and resource management. It is divided into four distinct functional areas that manage everything from base classes to dependency injection.

    To maintain a clean architecture, the Ship Layer (specifically the Containers Bay) should be kept slim and focused. It should only contain essential architectural components and must not include business-level reusable functionalities like Authentication or Authorization, as these are provided by the Containers themselves.

  8. How to adapt to multiple interfaces using Actions and Pluggable UIs

    master

    Porto supports multiple interfaces (e.g., WEB, API, and CLI) through a pluggable UI architecture. This allows you to develop business logic independently of the user interface.

    Core Principles for Adaptability:

    • Actions as the Center: Actions are the central organizing principle of the application, not the controller. Because Actions represent business logic, they can be shared across multiple UIs.
    • UI Separation: UIs are separated from the application business logic and are also separated from each other within each Container.
    • Workflow: Write the business logic (Actions/Containers) first, then implement the specific UI (WEB, API, or CLI) required to interact with that code.
  9. Understand the role and principles of Actions

    master

    In the Porto architecture, Actions represent the Use Cases of an application—the specific operations that can be performed by a user or other software.

    Key Principles:

    • Single Responsibility: Each Action should perform exactly one use case.
    • Task Orchestration: An Action's primary role is to orchestrate Tasks. It can retrieve data from one Task and pass it to another, or call multiple Tasks (including those from other Containers within the same Section).
    • Controller Interaction: Actions are typically invoked by Controllers. While they can be called by Event Listeners or Commands, they must not be called from Tasks.
    • Response Handling: Actions may return data to the Controller, but they should not return a response (e.g., HTTP responses). Returning responses is the responsibility of the Controller.
    • Reuse via SubActions: An Action should not call another Action directly. To reuse complex business logic that involves multiple Tasks, create a SubAction instead.
    • Interface: Every Action must implement a single public function named run().
    • Error Handling: Actions are responsible for handling all expected Exceptions.
    • Input: The run() function can optionally accept a Request Object as a parameter.
    class MyAction {
    
        public function run() {
            return [MyTask1, MyTask2, MyTask3]];
        }
    }
  10. Use Requests to handle user input, validation, and authorization

    master

    In Porto, Requests are used to encapsulate user input. They provide a mechanism to automatically apply validation and authorization rules to incoming data.

    Key behaviors:

    • Validation/Authorization Rules: A Request can hold specific rules that define what constitutes valid input or whether a user is authorized to perform the action.
    • Automatic Enforcement: When a Request is injected into a Controller, the framework automatically checks the input against the defined rules. If the data is invalid or the user is unauthorized, an Exception is thrown automatically.
    • Injection Point: To ensure automatic enforcement, Requests SHOULD only be injected into Controllers.
  11. Use Sections to group related Containers

    master

    A Section is a group of related containers that represents a bounded context or a sub-system (e.g., a micro-service).

    Key Benefits:

    • Isolation: Each section is completely isolated from others.
    • Scalability: Sections can be deployed separately (e.g., as independent micro-services).
    • Communication: Different sections communicate using Events and Commands.
    • Organization: Sections allow you to separate large, complex Models into smaller, context-specific ones (e.g., a Car model in a Race section vs. a Car model in a Lobby section).

    If your project is small, you can treat the entire application as a single section where all containers live in the root containers folder.

  12. Understand Porto's predictable dependency direction

    master

    Porto enforces a one-way dependency direction to prevent circular references and tangled imports. This structural constraint ensures that the dependency graph remains predictable as the codebase grows, making it easier to identify the impact of changes.

    Dependency Rules:

    • Containers: A Container can depend on other Containers. This is similar to how layers work in a layered architecture.
    • Within a Section: Containers within the same Section may depend on each other directly.
    • Between Sections: To maintain decoupling, Porto recommends using event-driven communication instead of direct calls. This approach facilitates easier extraction of a Section into a standalone Microservice in the future.

    By following these rules, you can determine exactly what will break when modifying a Container by checking only what depends on it in a single direction, rather than tracing mutual references across the entire codebase.