Architecting Evolvable Systems Using Hexagonal Architecture

A practical demonstration of Domain-Driven Design and Hexagonal Architecture, based on patterns I have applied in Enterprise Systems and Integrations.
Throughout my experience designing Enterprise Systems and Integrations, I have used Domain-Driven Design (DDD) and Hexagonal Architecture to build systems that need to evolve, integrate with multiple technologies, and remain maintainable over time.Domain driven design.
Architectural Influences
My approach to architecture has been shaped by several ideas that consistently appear in both enterprise systems and software craftsmanship:
Domain-Driven Design — keeping business concepts and rules at the centre.
Hexagonal Architecture / Ports and Adapters — isolating the domain and application from external technologies.
Clean Architecture — maintaining dependency direction and protecting higher-level business policies from lower-level implementation details. \
Two particularly influential references in my exploration have been: Tom Hombergs — his practical treatment of Hexagonal Architecture helped me think about Ports and Adapters not merely as a pattern, but as a way of creating explicit boundaries around an application. Robert C. Martin (Uncle Bob) — his work on Clean Architecture reinforced the importance of dependency direction, separation of concerns, and keeping business rules independent of frameworks and infrastructure.
Myfin is therefore not an attempt to reproduce either approach. It is my own small practical demonstration, using ideas that I have also applied in larger Enterprise Systems and Integrations.
Domain-Driven Design
DDD starts by putting the business domain at the centre. For Myfin, the domain includes concepts such as:
Expense
Category
Transaction
Monthly expenditure
Expense metrics
For example:
public record Expense(
LocalDate spentDate,
String expenseDetails,
Double withdrawlAmount,
Double depositedAmount,
String bankName) {
}
The Expense knows about an expense—not about Excel, files, frameworks, or persistence.
This separation is particularly important in enterprise systems, where business rules often need to survive changes in technology and integration platforms.
Importance of Hexagonal Architecture
Hexagonal Architecture, or Ports and Adapters, provides the boundaries that protect the domain from external technologies.
In Myfin, the Excel-specific implementation sits outside the application core.
The application depends on an ExpenseRepositoryPort, while the adapter provides the actual implementation.
So the dependency becomes:
Application → Port ← Adapter
rather than:
Application → Excel implementation
This is a pattern I have found particularly useful in enterprise integrations, where external systems and technologies change much more frequently than the underlying business capabilities.
Trade-offs
Hexagonal Architecture introduces intentional complexity. Instead of:
Service → Repository
we may have:
Service → Port → Adapter → Repository
That means:
More interfaces
More classes
More indirection
More dependency wiring For a small CRUD application, this can easily become over-engineering. The value appears when there are meaningful boundaries and expected changes.
My rule of thumb is: Don't create abstractions because the architecture demands them. Create them where change needs to be isolated.
Practical Application — Myfin
Myfin demonstrates this separation with a simple flow:
Bank Statement
↓
Excel Adapter
↓
Expense
↓
Categorisation
↓
Monthly / Yearly Metrics
The Excel adapter handles Excel-specific concerns such as reading rows and parsing dates.
Once the data becomes an Expense, the application works with the domain model.
Categorization and aggregation don't need to know that the original source was Excel.
Consider a future requirement:
"Instead of uploading Excel statements, retrieve transactions directly from a bank API."
The architecture allows us to introduce another adapter:
┌── Excel Adapter ──┐
│ │
├── CSV Adapter ────┤
│ │
└── Bank API Adapter┘
│
▼
ExpenseRepositoryPort
│
▼
Application
The application continues to work with the same domain concept:
Stream<Expense>
The business logic doesn't need to know where the data came from.
That's the practical value of Hexagonal Architecture: change can remain at the boundary.
Challenges with Hexagonal Architecture
My experience with enterprise systems has also taught me that Hexagonal Architecture is not simply about creating ports and adapters.
Finding the right boundaries - The most difficult question is often: What should actually be a port? Too few boundaries create coupling. Too many create unnecessary abstraction.
Preventing technology leakage - Domain objects should not gradually become dependent on: Databases, Frameworks, File formats, Messaging technologies, External APIs.
Managing complexity - As the number of adapters increases, dependency wiring and object lifecycle management become more complicated.
Final Thoughts
Myfin is not an attempt to demonstrate that DDD or Hexagonal Architecture is the "right" architecture for every application.
It is a small, concrete example of architectural practices that I have applied in much larger Enterprise Systems and Integrations, made public so that others can see and discuss the ideas through working code.
