Logo nexyo GmbH

nexyo GmbH

Startup

Go and DDD := Building the Right Thing

Description

Andreas Krimbacher von nexyo spricht in seinem devjobs.at TechTalk über die Entscheidungen, welche hinter der im Unternehmen angewandten Technologie Go und der Wahl der Architektur stehen.

By playing the video, you agree to data transfer to YouTube and acknowledge the privacy policy.

Video Summary

In "Go and DDD := Building the Right Thing," Andreas Krimbacher shows how combining Go with Domain-Driven Design and Clean Architecture underpins the long-lived, evolving Nexio Data Hub. He details modeling core domain entities (dataset, distribution, policy) and their lifecycle, using repository interfaces and factories to keep persistence out of the domain, and wiring presenters/HTTP endpoints, a use-case/app layer, and handlers with injected repositories. The payoffs—ubiquitous language, the Single Responsibility Principle, and a pragmatic testing setup (fast unit tests for entities, mockable repositories for integration plus GUI tests)—enable fast iteration, safe refactoring, and confidence in delivering the right features.

Go and DDD := Building the Right Thing — A practical architecture playbook from nexyo GmbH’s Data Hub

Context: A Data Hub at the center of an operative data ecosystem

In the session “Go and DDD := Building the Right Thing” by Andreas Krimbacher (Speaker: Andreas Krimbacher, Company: nexyo GmbH), we at DevJobs.at followed how a startup team frames an enterprise-grade product for longevity and evolution. The product in focus is the Nexio Data Hub: a component positioned in the middle of a data infrastructure, communicating with various microservices and databases, potentially with other hubs, offering a web UI, and exposing a CLI for automated control.

The engineering challenge is clear: build something that can live for 5, 10, even 20 years, while evolving with customer feedback. That demands conscious choices around language, architecture, and patterns that minimize accidental complexity and enable refactoring.

The team approached this with two questions: which technology, and which architectural/design patterns? The technology decision came quickly: Go. The architectural backbone was anchored in two books Krimbacher calls unavoidable: Clean Architecture by Robert C. Martin and Domain-Driven Design by Eric Evans. As he puts it, “there is no way around these two books.”

Why Go? The decision and the reasons

  • Team familiarity from earlier small- and mid-sized projects.
  • A language designed for simplicity and clarity, reducing the cost of change.
  • Strong refactoring friendliness, crucial for multi-year product evolution.
  • Security and concurrency characteristics that matter in a system constantly talking to others.

“Go was set,” Krimbacher summarizes. With that, the conversation turned from language to architecture: how to structure a long-lived system so it remains easy to reason about and safe to change.

DDD at the core: Domain objects and domain logic — no infrastructure details

The heart of Krimbacher’s approach is the domain. He models it with domain objects that are native to a Data Hub:

  • Dataset
  • Distribution
  • Policy

Domain logic lives on these objects—things like adding a distribution or deriving a combined policy. Importantly, this domain layer remains free of infrastructure details: no databases, no network protocols, no runtime specifics. The domain speaks the customer’s language and focuses solely on what must be done.

The domain lifecycle

Krimbacher highlights the lifecycle of a domain object:

  1. Create
  2. Persist
  3. Retrieve (from persistence)
  4. Modify
  5. Persist again
  6. Archive (end of lifecycle)
  7. Delete

From that lifecycle, two concrete needs emerge: something must create domain objects, and something must store and recover them. Enter Factory and Repository.

Repository pattern and Factory: Interfaces in the domain, implementation in infrastructure

The repository begins as an interface defined in the domain. It states, in domain terms, what must be possible—without any hint of how:

  • Persist a dataset
  • Retrieve a dataset by UUID
  • Delete a dataset

The actual implementation lives in the infrastructure layer; that’s where the database is accessed. Notably, the repository implementation receives a Factory via dependency injection. The Factory encapsulates all logic for creating bona fide domain objects, both for new instances and when rehydrating from stored representations. As a result, repositories can return fully-formed domain objects without leaking persistence concerns into the domain model.

This clean separation keeps the domain technology-agnostic while making infrastructure replaceable. It also sets up great testability and lowers the cost of refactoring.

Clean Architecture as the runtime skeleton: UI → presenter → use cases → entities

Krimbacher references the well-known Clean Architecture diagram but presents it in a linear, intuitive layout. From top to bottom:

  • UI (e.g., a web frontend)
  • Presenter (e.g., an HTTP/REST API)
  • Use-case layer (also called the application layer)
  • Entities (the domain—those domain objects described above)

Key observations:

  • The use-case layer receives a repository via injection. It orchestrates domain operations without knowing persistence details.
  • The presenter could be REST, gRPC, or something else. The domain remains unaffected by that choice.
  • Dependencies flow strictly from top to bottom. Entities know nothing about the upper layers. Use cases know nothing about repository implementations or who is calling the system.

This structure gives you the stability to evolve: swap a UI, change an API style, or adjust persistence—all without uprooting the domain.

How it comes together: main, handler, and the getDataset flow

Even without code on the screen, Krimbacher outlines the interplay clearly:

  • In main, infrastructure wiring happens: initialize a Factory, set up a Repository, compose the application.
  • Create a dataset handler (as part of the presenter/API layer).
  • Run the application as an HTTP server so the UI can consume it over REST.

The handler sits right at the intersection of repository, presenters, and entities. In a getDataset example:

  • The presenter invokes the method with a UUID.
  • The handler uses the injected repository to pull the dataset from persistence.
  • Domain logic can be applied if needed.
  • The result is returned to the UI.

The central message: keep the domain in the center, let the infrastructure orbit around it, and resist the urge to mix these concerns.

Why not quick CRUD over MySQL? Three principles that change the trajectory

Krimbacher addresses the common temptation: “Why not just stand up an HTTP API with a MySQL database and wire up basic CRUD?” His answer is anchored in three principles that combine DDD and Clean Architecture into a resilient strategy.

1) Ubiquitous language

DDD’s ubiquitous language establishes a shared understanding between domain experts and developers. As Krimbacher emphasizes, the domain layer is where the value is actually created. It stays focused on what matters to the customer, while all operational details are handled elsewhere. Because the domain is insulated from infrastructure, its model can evolve as the language and understanding evolve with stakeholders.

2) Single Responsibility Principle (SRP)

Referencing its definition in Clean Architecture: “A module should be responsible to one and only one actor.” Practically, if a package must absorb changes from multiple stakeholders—especially conflicting ones—its boundaries are wrong. SRP translates into easier refactoring, better testability, and fewer conflicts as requirements change.

3) Testability and the testing pyramid

Krimbacher states that when you ship a product, you must be “150% sure” it’s tested. The structure he outlines aligns with the testing pyramid:

  • Unit tests at the bottom: domain objects are free from databases and networks, ideal for fast, focused unit tests.
  • Integration/component tests in the middle: thanks to clean interfaces (e.g., the repository defined in the domain), the infrastructure can be mocked or replaced with test instances. You don’t need a real database in these tests.
  • GUI tests at the top: to stitch everything together and validate the user experience.

This approach builds trust in refactoring: with strong tests, you can evolve the system without fear.

Team and product effects: onboarding, speed, and safe refactoring

The benefits extend beyond code:

  • Decoupled packages and clean structure accelerate onboarding. New team members can find the right location for features and understand the architecture quickly.
  • Close collaboration with customers is part of the process. When feedback arrives, the team must adapt quickly. Safe refactoring is crucial; without it, valuable changes would be postponed or avoided.
  • With solid tests and clear boundaries, the team can deliver fast and iteratively. That’s Krimbacher’s essence of “Building the Right Thing”: shipping quickly while staying on the right product path, enabled by architectural discipline and test-backed evolution.

Practical guardrails we took from the session

Without inventing new context, Krimbacher’s points translate into actionable steps:

  • Model the domain first
  • Define domain objects (e.g., dataset, distribution, policy) and attach domain logic.
  • Keep infrastructure concerns out of the domain layer.
  • Clarify the lifecycle of domain objects
  • Create, persist, load, modify, archive, delete.
  • Make responsibilities for each step explicit.
  • Define the repository as a domain interface
  • Express operations in domain terms (persist, load by UUID, delete).
  • Avoid implementation details in the interface.
  • Use a factory for creation and rehydration
  • Encapsulate creation logic and inject the factory into repository implementations.
  • Rebuild full domain objects from persisted representations.
  • Use Clean Architecture as the runtime scaffold
  • Treat UI → presenter → use cases → entities as a strict dependency flow.
  • Let use cases orchestrate, keep entities pure, and expose functionality via a presenter (e.g., REST).
  • Keep knowledge of infrastructure in the upper layers; entities stay ignorant of it.
  • Compose in main
  • Initialize factory and repository, compose the application.
  • Create presenters/handlers and start the HTTP server so the UI can consume it.
  • Treat handlers as the seam
  • Handlers bind repository, presenter, and domain.
  • In a flow like getDataset: UUID comes from the presenter, repository loads, domain stays central, UI gets the result.
  • Feed the testing pyramid deliberately
  • Many fast unit tests in the domain.
  • Integration/component tests via interfaces (repository) without a real DB.
  • GUI tests for end-to-end behavior and UX.
  • Use SRP as a refactoring compass
  • If multiple actors push changes into the same package, reslice boundaries.
  • Maintain the ubiquitous language
  • Use domain terms consistently in code.
  • Let the language co-evolve with customer understanding.

Closing: Go + DDD + Clean Architecture for long-lived enterprise systems

“Go and DDD := Building the Right Thing” illustrates why the seemingly slower path—model the domain carefully, enforce dependency rules, elevate interfaces, and honor the testing pyramid—turns out to be the faster, safer path over the lifespan of a product. The Nexio Data Hub Krimbacher outlines runs on a simple idea: the domain is the heart; infrastructure is replaceable; the application layer orchestrates; the presenter exposes.

Choosing Go is a natural fit in this story: simplicity, refactorability, and concurrency support suit a hub that sits at the center of many moving parts. Together with DDD and Clean Architecture, the result is a system that can evolve with customer needs—testable, refactorable, and clear enough that new engineers can navigate it quickly. For us, that’s the core meaning behind “Building the Right Thing”: structure, language, and tests enable sustainable evolution.

For engineers working on long-lived products, the path is straightforward: take the domain seriously, respect Clean Architecture boundaries, place repositories and factories deliberately, inject dependencies explicitly, prioritize tests, and live the UI → presenter → use cases → entities flow with discipline. That’s how a language decision plus two books becomes a durable engineering approach.

More Tech Talks

More Tech Lead Stories

More Dev Stories