Recent Posts
Archives

Posts Tagged ‘CleanArchitecture’

PostHeaderIcon [MunchenJUG] Architectural Decoupling: Practical Implementations of Persistence in Clean Architecture (13/May/2024)

Lecturer

Daniel Istvan Buza is a Senior Software Engineer and Technical Lead with extensive experience in the Java ecosystem. Currently leading two development teams, Daniel focuses on mentoring, code reviews, and hosting coding dojos to promote high-quality software craftsmanship. His technical expertise encompasses a wide array of technologies, including Microservices, Spring, Angular, Kafka, and MongoDB. Beyond his professional role, he is a frequent contributor to technical discourse, sharing insights through platforms like DZone and GitHub.

Abstract

While the theoretical foundations of Clean Architecture have been widely discussed since its inception by Robert C. Martin, practical implementation details—particularly concerning persistence—often remain elusive. This article examines the methodologies for decoupling business logic from technical infrastructure within Java-based systems. It explores the “dependency rule,” the strategic value of the business core, and the implications of making the domain agnostic of specific languages or frameworks. Central to this analysis is a non-standard, property-based approach to defining persistence APIs, designed to enhance modularity and maintainability in complex software environments.

The Philosophical Core of Clean Architecture

The essence of Clean Architecture lies in the strict management of dependencies, where the business core remains isolated from external technical influences. Robert Martin, often referred to as Uncle Bob, formalized this concept in 2012, emphasizing that business logic should not depend on the database, UI, or even the underlying programming language.

This decoupling is not merely a technical preference but a strategic business decision. In a software project, the primary value resides in the business rules. A system with a fully functional business core but no UI or database is more valuable and marketable than a system with a polished interface but no logic. By treating the business core as the primary asset, developers can defer decisions about specific frameworks or storage technologies, ensuring the system remains flexible and adaptable to future requirements.

Language and Framework Independence

A rigorous application of Clean Architecture suggests that the domain should ideally be expressed in a way that is independent of specific programming languages. While most systems are implemented in languages like Java or Python, some specialized environments, such as tax office systems, utilize domain-specific languages (DSLs) to encapsulate complex rules. This approach ensures that changes in the technical stack—such as upgrading a framework or migrating to a different runtime—do not force a redesign of the business logic.

Property-Based Persistence APIs

A significant challenge in implementing Clean Architecture is the interface between the domain and the persistence layer. Traditional approaches often couple the domain to specific database structures. A more flexible methodology involves defining a persistence API based on properties rather than fixed entities. This allows the domain to specify what data is needed without prescribing how it should be stored or retrieved.

By utilizing Java language features effectively, developers can create persistence abstractions that are both expressive and decoupled. This modularity facilitates easier testing, as the business logic can be verified in isolation using mocks or in-memory repositories without the overhead of a real database.

Conclusion

Transitioning from the theory of Clean Architecture to a sustainable implementation requires a disciplined approach to dependency management. By prioritizing the business core and utilizing property-based persistence abstractions, development teams can build systems that are resilient to technical churn. The ultimate goal is to create a codebase where the most valuable part of the software—its logic—is protected from the inevitable evolution of external tools and frameworks.

Links:

PostHeaderIcon [VoxxedDaysBucharest2026] Ports, Adapters, and the Independence of Business Logic: George Patrașcu on Hexagonal Architecture in Practice

Lecturer

George Patrașcu is a seasoned software engineer at eMAG/CTO with more than twenty years of professional experience, including significant time in architectural leadership positions. Currently contributing to the Invoice and Payments platform team, George plays an active role in shaping internal developer guidelines and promoting sound architectural practices throughout a large organization characterized by hundreds of autonomous teams and diverse technology stacks.

Abstract

Within expansive microservices ecosystems featuring autonomous teams, frequent deployments, and heterogeneous technologies, business logic commonly becomes entangled with infrastructure specifics, yielding systems that are difficult to maintain, evolve, or understand. George Patrașcu presents Hexagonal Architecture—also recognized as Ports and Adapters—as a pragmatic methodology for protecting core domain logic from external dependencies including relational databases, RESTful services, event streams such as Kafka, and various third-party integrations. Grounded firmly in production realities at eMAG, the session provides balanced coverage of conceptual foundations, detailed C# implementation examples, testing approaches, and honest discussion of trade-offs encountered in practice.

The Challenges of Traditional Layered Architectures in Evolving Systems

Conventional layered architectures consisting of presentation, business, and data access tiers deliver initial value for straightforward applications. However, sustained growth across hundreds of teams utilizing varied languages (Java, .NET, Python, Scala), communication mechanisms (REST, Kafka), and continuous integration practices exposes fundamental weaknesses.

Business rules gradually permeate multiple layers: service classes reference persistence entities directly, controllers embed data access logic, and external service contracts influence domain models. Bounded contexts, a cornerstone of Domain-Driven Design, demand careful translation through anti-corruption layers, yet traditional designs frequently fail to maintain clean separation.

Resulting issues include duplicated business logic across backend-for-frontend components, mobile client platforms, and core services; severely compromised unit testing due to pervasive infrastructure dependencies; and cascading changes whenever external contracts, database schemas, or framework versions evolve. Simple folder structures offer no enforceable boundaries, while multi-module projects introduce tedious mapping layers that increase cognitive load.

Core Concepts of Hexagonal Architecture: Ports, Adapters, and the Application Core

Hexagonal Architecture fundamentally inverts dependency direction to position the domain model at the center. Business logic and associated domain entities reside within the core, depending exclusively upon abstract ports rather than concrete implementations. Input ports, implemented by driving adapters (REST controllers, message consumers), expose use cases to external actors. Output ports, realized by driven adapters (repositories, external clients), allow the core to interact with infrastructure without awareness of underlying details.

This structure adheres strictly to the Dependency Inversion Principle: high-level policy (domain) remains independent of low-level mechanisms (infrastructure). The hexagonal representation visually encapsulates the core, with ports serving as interfaces through which adapters connect. Domain objects and language remain pure, expressed in business terms rather than technical artifacts.

Flexible structuring accommodates varying scales: monolithic single projects for initial simplicity, separation of adapters by technical domain (isolating payment processors or external APIs), or evolution from modular monoliths toward independent microservices. This “build modular from the start” philosophy supports rapid feature development followed by natural service extraction when boundaries emerge.

Implementation Details, Trade-offs, and Testing Strategies

Practical development begins with domain modeling. For a rescue fleet management system, an AssembleFleet input port defines the primary use case contract. Implementation within a domain service orchestrates inventory retrieval and selection logic expressed purely in business concepts.

Driven adapters address external complexities: a Swappy client manages API pagination, performs type coercions (string passenger counts to domain integers), and implements anti-corruption filtering for inconsistent partner data. Custom domain annotations (@DomainService) facilitate integration with dependency injection frameworks without introducing technical concerns into the core.

Persistence strategies favor rich domain models mapped via ORM capabilities (e.g., shadow properties, complex types), minimizing dual maintenance of entities. This preserves core purity while leveraging framework strengths.

Testing follows clear separation: exhaustive unit tests exercise domain logic in complete isolation, while integration tests utilize fakes and mocks for adapters, verifying translation correctness without external system dependencies.

Trade-offs warrant careful consideration. The pattern excels with rich domain models containing substantial behavior but may introduce unnecessary indirection for straightforward query-dominant services. Hybrid approaches applying hexagonal principles selectively to complex logic paths, combined with CQRS separation or vertical slice organization, often prove optimal. The speakers caution against architectural dogmatism—context, team maturity, and problem complexity should guide application extent.

Architectural fitness functions, implemented via tools like ArchUnit, provide automated verification of boundaries even when AI assistance generates code.

Implications for Autonomous Teams and Long-Term Maintainability

Within large organizations featuring hundreds of developers operating with significant autonomy and minimal centralized control, Hexagonal Architecture strikes an effective balance. Teams retain freedom in implementation details while benefiting from consistency promoted through architecture guilds and shared guidelines. The pattern facilitates technology migration, incremental refactoring, and clear delineation of responsibilities.

By maintaining domain logic independent of infrastructure specifics, services demonstrate greater resilience to external changes—whether API contract updates, database platform shifts, or new integration requirements. This aligns naturally with trunk-based development, feature flag strategies, and high-frequency deployment practices, enhancing overall organizational agility.

Not every service necessitates complete hexagonal purity. Selective application targeting areas of highest coupling delivers outsized returns in testability, evolvability, developer onboarding speed, and long-term maintenance costs.

Links:

PostHeaderIcon [SpringIO2023] Anatomy of a Spring Boot App with Clean Architecture: Steve Pember

In a thought-provoking session at Spring I/O 2023, Steve Pember, a seasoned developer from Boston-based startup Stavi, explored the principles of Clean Architecture and their application within Spring Boot applications. Drawing from Robert Martin’s influential book, Steve demonstrated how Clean Architecture, inspired by patterns like Ports and Adapters and Hexagonal Architecture, fosters readable, flexible, and maintainable codebases. Through a reference application and practical insights, he provided a roadmap for developers to structure Spring Boot apps that remain resilient to change and scalable for large teams.

The Case for Software Architecture

Steve began by addressing the often-misunderstood role of software architecture, challenging the stereotype of architects as mere whiteboard enthusiasts. He likened software architects to their building counterparts, who design every detail from high-level structures to minute specifics. Without proper architecture, Steve warned, systems devolve into unmaintainable “big balls of mud,” slowing development and hindering competitiveness. He highlighted the benefits of well-architected systems—separation of concerns, modularity, testability, and maintainability—arguing that these guardrails enable teams to maintain velocity over time, even if they initially slow development.

Principles of Clean Architecture

Delving into Clean Architecture, Steve outlined its core concepts: SOLID principles, component design, boundaries, and dependency rules. He clarified SOLID principles, such as single responsibility (supporting one user type per class) and dependency inversion (using interfaces), as foundational to clean code. Components, he explained, should be independently developable and loosely coupled, aligning with domain-driven design or microservices. The defining feature of Clean Architecture is its layered structure, where dependencies point inward to a core of business logic, encapsulated by interfaces that shield it from external details like databases or third-party services. This ensures the core remains agnostic, enhancing flexibility and testability.

Implementing Clean Architecture in Spring Boot

Steve demonstrated how to apply Clean Architecture in Spring Boot using a reference shoe store application. He proposed a multi-module structure with three components: core (housing business logic, entities, and services), details (containing database and third-party integrations), and app (where Spring configuration and integration occur). By using interfaces for repositories and gateways, the core remains independent of external systems, allowing seamless swaps, such as replacing a PostgreSQL repository with DynamoDB. Steve emphasized minimal controllers and service classes, advocating for specific, single-responsibility services like CustomerOrderQueryService. He also highlighted the importance of integration tests, using tools like Testcontainers to validate interactions with external systems.

Treating Details as Deferrable

A key takeaway was Steve’s mantra that “everything not in core is a detail.” Databases, environments, input mechanisms, and even Spring itself should be treated as implementation details, deferrable until necessary. He cautioned against premature database schema design, urging developers to prioritize business logic over storage concerns. By encapsulating details behind interfaces, applications become adaptable to changes, such as switching databases or input methods (e.g., HTTP to Kafka). Steve’s demo showcased this flexibility, swapping a PostgreSQL order repository for DynamoDB with minimal code changes, proving the power of Clean Architecture’s plug-in approach.

Links: