← Back to home
14 min readv1.0

ARC-DDD: A Domain-Driven Design Approach That Started in Australia

Arda Akgür

DDDARC-DDDARC DDDAustralian DDDDomain-Driven DesignBounded ContextSoftware Architecture

Most ideas in software architecture do not appear fully formed from nothing.

A principle an engineer learns takes a different shape when they meet another problem. Sometimes a small idea grows over years — applied, broken, redesigned — and eventually becomes its own architectural approach.

That is how the story of ARC-DDD began.

I started developing the first versions of this approach while working as an Associate Engineer in Australia.

But when I explain where the idea came from, I especially need to give credit to one person:

Victorian Software CEO Adrian Budiman.

Adrian was the first person who showed me the idea of splitting a system into horizontally separate domains, instead of thinking about domains only as classic architectural layers.

That idea became my starting point.

I later carried this approach much further in systems I built myself.

Today ARC-DDD, or as I sometimes call it Australian DDD, is the result of that process.

What Is the Problem in Classic DDD?

Classic Domain-Driven Design usually splits a system into vertical layers:

  • Presentation
  • Application
  • Domain
  • Infrastructure

That approach is extremely valuable, and ARC-DDD does not try to eliminate it.

What I added is a second axis.

While the system is split into vertical layers, the domain model itself is also split into horizontal bounded contexts.

For example:

Plain Text
Core├── Users├── Events├── Transactions├── Vendors└── Payments

The fundamental rule here is this:

One domain should not know another domain's entire world.

This is where ARC-DDD's real character emerges.

A User Is Not the Same User for Every Domain

One of the most common problems I see in large systems is enormous entity models.

For example, there is a single User class in the system.

Over time, everything gets added into it:

Plain Text
PasswordPhoneAddressBalanceIBANKYCPermissionsProfileSecurityTransactionsPreferences...

Then the Events domain starts using the entire User model even when it only wants to show the user's name and photo.

The Transactions domain uses the same model for balance and IBAN.

The Vendor system uses the same model again.

In the end, a system that looks domain-separated in theory becomes, in practice, one giant structure tied together through the same entities.

ARC-DDD takes a different approach here.

For example:

Plain Text
Users.UserEvents.User2Transactions.User3

These are different domain models.

But they can represent the same physical database table.

Plain Text
Users.User          ──┐Events.User2        ──┼──> Users tableTransactions.User3 ──┘

If the Events domain only needs:

Plain Text
UserIdFirstNameFamilyNamePhoto

User2 contains only those.

If the Transactions domain needs:

Plain Text
UserIdBalanceIBANVerified

fields, then User3 is defined according to its own domain needs.

Each domain sees the world only as far as it needs to.

That is exactly the core approach of ARC-DDD: bounded contexts are separate, and instead of carrying another domain's full aggregate, you create a domain-local projection that contains the fields you need. When necessary, that projection is mapped to the same physical table.

Same Database, Different Domain Realities

One important characteristic of ARC-DDD is that it does not treat separate databases as mandatory for creating bounded contexts.

For example, with Entity Framework Core:

C#
modelBuilder    .Entity<User2>()    .ToTable("Users")    .HasKey(x => x.UserId);

User2 in the Events domain can still bind to the physical Users table.

But because the model only contains the properties the Events domain needs, that domain does not drag unnecessary user fields into its own model.

In the public reference implementation (eski-dev), the Goldtag example shows User, User2, and User3 binding to the same Users table from different domain perspectives. User2 represents the data and behavior Events needs; User3 represents what Transactions needs.

This gives us an important balance:

The database can be physically shared, but domain models do not have to be shared.

That is one of ARC-DDD's core principles.

Each Domain Owns Its Own Persistence World

This separation does not stop at the folder level.

Each bounded context has its own DbContext structure.

For example:

Plain Text
UserDbContextEventsDbContextTransactionDbContextVendorsDbContext

The Events domain knows its own projection models.

The Transactions domain knows its own models.

The Users domain owns the canonical User model.

In the public reference implementation (eski-dev), for example, UserDbContext uses the canonical User model, EventsDbContext uses the User2 projection, and TransactionDbContext uses the User3 projection within its own context.

For that reason, one domain does not directly import another domain's Core models.

Inside Events:

C#
using Gold.Core.Users;

instead of pulling the entire User aggregate into the system, Events creates its own projection.

This is deliberate duplication.

Because in ARC-DDD, sometimes:

A small amount of duplication is cheaper than strong coupling.

One of the core rules at the Core level is that domain folders do not import each other directly. For example, instead of using Gold.Core.Users, Events creates its own local representation such as User2.

Where Do Domains Meet?

In real applications, bounded contexts cannot live in complete isolation.

A single operation may need multiple domains at once, such as:

  • Ordering
  • Payments
  • Users

ARC-DDD does not connect domain layers to each other here. Instead, it moves composition to the outer edge of the system.

For example:

Plain Text
API | +--> OrderingRepository | +--> PaymentsRepository | +--> UsersRepository

In other words, cross-domain orchestration happens at the API or application level.

Core domains do not import each other and create a dependency chain.

In eski-dev as well, cross-domain composition is done on the API side. A controller or application service can work with multiple repositories when needed, but Core domains are not wired to each other.

This separation becomes very valuable as the system grows.

Three Core Projects

If I were creating a new ARC-DDD system today, I would keep the basic structure quite simple:

Plain Text
Product.CoreProduct.DomainProduct.Api

Dependency direction:

Plain Text
Product.ApiProduct.DomainProduct.Core

Core contains pure domain models.

It has no Entity Framework or infrastructure dependency.

Domain contains the persistence layer, DbContexts, and repository structures.

Api acts as the composition root and orchestrates different bounded contexts within the same use case when needed.

The dependency direction in eski-dev's current reference structure is also Gold.Api → Gold.Domain → Gold.Core. Keeping the Core layer free of infrastructure packages is one of the fundamental rules.

A One-Sentence Definition of ARC-DDD

If I had to describe this architecture in a single sentence today:

ARC-DDD is a software architecture approach that extends classic layered DDD with horizontal bounded contexts and domain-specific entity projections over shared tables.

I define it more technically as:

Classic layered DDD + horizontal bounded-context folders in Core + per-context entity projections over shared tables.

But for me, the more important idea behind this approach is this:

A domain should not see all the knowledge that exists in the system — only the world it needs to do its own work.

Why "Australian DDD"?

Because for me, the origin of the approach goes back to Australia.

The horizontal domain-separation idea I learned from Adrian Budiman while working as an Associate Engineer was the starting point.

I later expanded that idea by applying it in my own systems — with domain-local entity models, shared-table projections, per-domain DbContexts, and controlled cross-domain composition principles.

So when I talk about ARC-DDD, I do not think it is right to present it as "I invented everything from scratch."

Engineering usually does not progress that way anyway.

Someone gives you a strong idea.

You apply it to other problems.

You see what is missing.

You add new principles on top.

And years later, a system emerges that is quite different from the original idea.

For me, ARC-DDD developed exactly that way.

The horizontal domain thinking Adrian showed me was the spark.

My contribution was turning that spark into a practical architectural model that today I call ARC-DDD.

Public Reference Implementation

ARC-DDD is not only an architectural approach I describe in theory.

For anyone who wants to see how the approach is applied inside a real system, I have published one of my older development codebases publicly:

GitHub:
https://github.com/Arcnaboo/eski-dev

The repository is public, C#-based, and published under the MIT license.

This repository is the public Goldtag legacy backend reference implementation for ARC-DDD.

The repository explicitly defines the reference implementation as:

Plain Text
Gold.CoreGold.DomainGold.Api

Inside the repository, I especially recommend looking at these models:

Plain Text
Gold.Core/Users/User.csGold.Core/Events/User2.csGold.Core/Transactions/User3.cs

These show ARC-DDD's most important idea directly.

Canonical User belongs to the Users bounded context.

Events uses a narrowed User2 model shaped for its own needs for the same user.

Transactions uses a User3 model suited to its own operations.

You can see the same approach in models such as Event and Event2, Notification and Notification2. Look at these files in the repository especially when learning the reference implementation.

An important distinction needs to be made here.

This repository carries the traces of an old, real production codebase.

So I would not use every implementation detail in it one-to-one in a system I would write from scratch today.

For example, old connection-string approaches, some controller structures, incomplete dependency-injection usage, and legacy pieces that are no longer needed are not ARC-DDD itself.

I call out this difference especially:

Take the ideas, modernize the mechanics.

The lasting idea:

Horizontal bounded contexts + per-domain projections + per-domain DbContexts.

Not the historical implementation details of the old code.

Why Am I Sharing This Publicly?

I do not think it is enough to explain an architectural approach with diagrams alone.

People need to be able to:

  • open real code,
  • compare entities,
  • see DbContext mappings,
  • inspect repository boundaries,
  • criticize the approach

That is why I want both the architectural documentation and a real public reference implementation for ARC-DDD to stay accessible.

Repository:

https://github.com/Arcnaboo/eski-dev

But I do not want you to copy every line in the repository in order to understand ARC-DDD.

Quite the opposite.

Separate the parts that come from the age of the code.

Look at the domain boundaries.

Look at how the same physical entity is modeled differently across different bounded contexts.

Look at how the contexts are separated.

Look at why Core does not import other domains' models.

Because the essence of ARC-DDD is not a framework or a specific technology.

Entity Framework can change.

.NET can change.

The database can change.

The repository pattern can change.

Years from now, most of the tools we use today may have changed as well.

But the fundamental principle does not change:

Make domain boundaries real boundaries in code.

And show each domain only the world it needs.