Skip to content

Internal Architecture Design Principles

This document covers Functorium’s internal architecture design principles. Starting from the definition of software architecture, it explains the entire architecture — including layer structure based on the separation of concerns principle, dependency direction, testing, and observability — with visual aids. It serves as a reference for developers new to architecture design as well as teams looking to apply Functorium to existing projects.

“When starting a new project, have you ever defined the folder structure first, only to find that business code and technical code became intertwined, making maintenance difficult?” “Have you ever had to modify business logic when replacing a technical framework?” “Have you ever given up writing tests because of external dependencies?”

The core of architecture design lies in layer structure and dependency direction rules based on the separation of concerns principle. By clearly separating business concerns from technical concerns and maintaining dependency direction from outside to inside, each concern can be developed and tested independently.

Through this document, you will learn:

  1. Software Architecture Definition - Why architecture is a core structural decision, not just a folder structure
  2. Internal/External Architecture Distinction - Separation of concerns at the service level and layer level
  3. Layer Responsibilities and Dependency Direction - Roles and dependency relationships of Domain, Application, and Adapter layers
  4. Testing and Observability Placement - Where unit tests, integration tests, and Observability sit within the architecture

The core of architecture design is separating business concerns from technical concerns and maintaining dependency direction from outside to inside.



Software architecture is not just a folder structure. It is a core structural decision of the system, and because the cost of change is high, it must be carefully designed early. The following figure shows what software architecture is and why it matters.


Application architecture has evolved from monolithic to microservices, and back to modular monolith. Each stage addresses different problems with different trade-offs. Functorium, drawing on lessons from this architectural evolution, focuses on Internal architecture to provide a structure that can be combined with any External architecture.

Application External(Outer) / Internal(Inner) Architecture

Section titled “Application External(Outer) / Internal(Inner) Architecture”

  • Separation of concerns principle

    CategoryUnit of ConcernPhysical Entity
    External ArchitectureServiceContainer
    Internal ArchitectureLayer-
    Object-OrientedObjectClass

The core of Internal architecture is separation of concerns. When you clearly distinguish business logic (what to do) from technical details (how to implement it), each can be developed and tested independently. Functorium concretizes this principle into three layers.

Internal architecture separates technical concerns from business concerns according to the separation of concerns principle. These separated concerns are defined and managed as layers.

Concern CategoryLayer NameLayer Role
Technical ConcernAdapter LayerTechnical flow and units (input/output handling)
Business ConcernApplication LayerBusiness flow (Use Case)
Business ConcernDomain LayerBusiness units (Entity)
  • Only business concerns explicitly separate units and flows into distinct layers.
  • The dependency direction points from outside to inside so that technical concerns (Adapter Layer) depend on business concerns.
  • This allows business concerns (Application/Domain Layers) to be developed and tested without depending on technology.


Dependency direction is one of the most important rules in architecture. If dependencies point from inside (business) to outside (technology), changing the database requires modifying business logic as well. Functorium enforces dependencies to always point from outside to inside through Dependency Inversion (DIP).

App = Driving interface definition and implementation + Driven interface definition and usage

  • Driving Adapter (Input): Uses the App provided i/f interface.
  • Application: Implements the App provided i/f interface and uses the App required i/f interface.
  • Driven Adapter (Output): Implements the App required i/f interface.

Through Dependency Inversion (DIP), the Adapter Layer depends on interfaces defined in the Application Layer, so the dependency direction points from outside to inside.

  • Clean Architecture

  • Hexagonal Architecture

  • You can confirm that the dependency direction is the same as Clean Architecture and Hexagonal Architecture.


The final Internal architecture integrates everything discussed so far — separation of concerns, layer structure, and dependency direction — into one view. You can see which patterns each layer uses and where testing and observability are positioned.

CategoryLayerPatternDescription
Input (Request)AdapterResult PatternReturns results in T(success)/Error(failure) form
Output (Response)AdapterStrategy Pattern, FunctionalExternal system integration through IObservablePort interface
Business OperationApplicationMediator Pattern (CQRS)IRequest/IResponse interfaces (ICommandRequest, IQueryRequest)
Business UnitDomainFunctionalCore business logic and entities

  • Unit Tests: Test only business concerns.
  • Integration Tests: Test including technical concerns.


This document focuses on Internal architecture. External architecture (inter-service communication, container orchestration, API Gateway, etc.) will be covered in a separate document.


Q1. Why should technical concerns and business concerns be separated?

Section titled “Q1. Why should technical concerns and business concerns be separated?”

Technology (frameworks, databases, external APIs, etc.) can change over time. If business logic is tightly coupled to specific technology, changing the technology requires modifying business logic as well. Separating concerns means technology changes do not affect business logic, improving maintainability.

Additionally, independent development becomes possible. Even when technical concern (Adapter) implementation is not complete, business concerns (Application/Domain) can be developed and tested independently. For example, even if DB integration is not ready, you can proceed with business logic development using in-memory implementations or unit tests.

Q2. What is the difference between Driving Adapter and Driven Adapter?

Section titled “Q2. What is the difference between Driving Adapter and Driven Adapter?”
CategoryDriving Adapter (Input)Driven Adapter (Output)
RoleThe entity that calls the applicationThe target that the application calls
ExamplesREST Controller, CLI, Message ConsumerRepository, External API Client, Message Publisher
InterfaceUses App provided i/fImplements App required i/f

Q3. Why should the dependency direction point from outside to inside?

Section titled “Q3. Why should the dependency direction point from outside to inside?”

If the dependency direction points from inside (business) to outside (technology), business logic depends on technology. In this case, changing technology requires changing business logic as well. Conversely, if dependencies point from outside to inside, technology depends on business, so you only need to replace the technology.

Q4. Why separate the Application Layer and Domain Layer?

Section titled “Q4. Why separate the Application Layer and Domain Layer?”
  • Application Layer: Handles business flow (Use Case). Defines “what to do in what order.”
  • Domain Layer: Handles business units (Entity). Defines “what has which rules.”

Separating flow and units means Use Case changes do not affect Entities, and Entity rule changes have minimal impact on Use Case flow.

Q5. How does this architecture relate to Clean Architecture and Hexagonal Architecture?

Section titled “Q5. How does this architecture relate to Clean Architecture and Hexagonal Architecture?”

All share the same dependency direction (from outside to inside). Terminology and expression methods differ, but the core principles are the same:

This DocumentClean ArchitectureHexagonal Architecture
Adapter LayerFrameworks & DriversPort & Adapter
Application LayerUse CasesApplication
Domain LayerEntitiesDomain

Q6. Why is the Adapter excluded from unit testing?

Section titled “Q6. Why is the Adapter excluded from unit testing?”

Adapters are connected to external technology (DB, HTTP, file system, etc.), making test environment setup complex and execution slow. Testing only business logic (Application/Domain) enables fast and stable unit tests. Adapters are verified through integration tests.