Trends in modern software architecture

...and how to avoid them

Overview

  • Hexagonal Architecture
  • Reactive Programming Models
  • CQRS
  • Event Sourcing
  • Summary

Hexagonal Architecture

What is it?

Layered

Hexagonal

Promise

  • Protect your core domain model against leaking
  • Outer layers can never force changes to the core
  • Allows applications to specify required interface
  • Easy to replace adapters with mocks for testing

Pitfalls

You end up with lots of interfaces and duplications that constantly change during development, creating a hard-to-maintain codebase

Alternatives

  • What do you want to achieve?
    • Protect core domain → Why ports & adapters?
    • Enforce architectural rules → ArchUnit
    • Replace adapters for testing → Mockito
  • Apply abstractions where they make sense
  • Apply common sense everywhere else

Conclusion

  • Preventing your core domain model from leaking to the web is common sense
  • Dozens of interfaces with exactly one implementation are ridiculous
  • Read "Hexagonal Architecture Explained"

Reactive Programming Models

What is it?

  • Classic, blocking programming models
      → Thread per Request-Model
      → Methods return results
  • Reactive programming models
      → Event Loop Model
      → Callback functions or Futures

Promise

  • Threads not blocked by downstream work
  • Very responsive applications
  • Tens of thousands of requests in seconds

Pitfalls

  • Very different programming model
  • Very steep learning curve
  • Blocking downstream APIs

Alternatives

  • Consider expected load scenarios before making design decisions
  • In most cases, horizontal scaling might prove to be more cost-efficient
  • In Java, Virtual Threads might make reactive APIs obsolete
  • spring.threads.virtual.enabled=true

Conclusion

From the Spring WebFlux documentation:

We expect that, for a wide range of applications, the shift is unnecessary.

CQRS

What is it?

  • Command-query responsibilty segregation
  • Separate write- & read-models

Promise

  • APIs with asymmetric read-write load
  • APIs where queries require computed outputs
  • Message-driven APIs

...all benefit from separate models

Pitfalls

Maintaining separate models, along with controller classes, business logic, persistence layers etc, leads to far more complex software projects

Separate models implicitly lead to eventual consistency (stale reads)

Alternatives

Always consider using a plain CRUD API first

Start CRUD API, evolve a separate write API over time, keeping the CRUD API for queries

Conclusion

From Greg Young's blog:

CQRS is not a silver bullet
CQRS is not a top level architecture
CQRS is not new
CQRS is not shiny
CQRS will not make your jump shot any better
[...]
CQRS can open many doors.

Event Sourcing

What is it?

Object Persistence

Event Sourcing

Promise

Focus on change instead of state makes building reactive systems easier

All domain objects implicitly have a history

Pitfalls

Very complex persistence pattern

Usually implemented along with CQRS, leading to exponentially more complex software systems

When used with CQRS, the aggregates aren't allowed to answer queries

Very steep learning curve

Alternatives

Event-driven systems can easily be build using simple persistence patterns

When a history of domain objects is required (eg, for auditing), consider using Envers

Conclusion

Unless you're building a real-time stock trading system, don't do it

The quest for simplicity

The quest for simplicity

Do the simplest thing that could possibly work

Non-functional requirements matter!

Consider the available skill set

Mind Conways law

Highly aligned, loosely coupled

Make sure technical and business goals align

Thank you!