2 min read

Sharing Bounded Context for Startups

A DDD approach that ensures team scalability without oversplitting domain boundaries too early.

Sharing Bounded Context for Startups thumbnail

Introduction

Splitting the Bounded Context at the startup stage may seem like overkill. However, if the initial domain boundary is not clear, terminology conflicts and responsibility confusion will repeat as the team increases. This article introduces a DDD boundary setting method that can be applied even to small organizations.

스타트업을 위한 Bounded Context 나누기 커버
Wikimedia Commons 기반 무료 이미지

Problem definition

If you quickly add features without context, the following problem will repeat itself.

  • The same word has different meanings for each team, increasing communication costs.
  • The data model is expanded to a general-purpose table, increasing the scope of impact when changes are made.
  • Distribution risk increases as permissions, payment, and content policies are mixed in one service.

In the beginning, you can start with language and separation of responsibilities rather than complete separation. Create a context map first and gradually adjust the code boundaries.

Key concepts

perspectiveDesign criteriaVerification points
languageUbiquitous language definition by teamFrequency of term conflict
modelSeparate entities by contextModel change ramifications
IntegrationACL (anti-corruption layer)Boundary Invasion PR Rate
organizationAligning team responsibilities and code boundariesDecision-making speed

In startups, it is more realistic to distinguish between core domains and support domains rather than dividing the boundaries too much. What's important are naming conventions and interface contracts.

Code Example 1: Bounded Context Contract

// Billing Context
export type BillingInvoice = {
  invoiceId: string;
  accountId: string;
  totalAmount: number;
  status: "issued" | "paid" | "void";
};

// Content Context
export type ContentPost = {
  postId: string;
  authorId: string;
  title: string;
  visibility: "public" | "private";
};

Code example 2: ACL adapter

export class BillingAcl {
  constructor(private readonly billingClient: BillingApiClient) {}

  async getActivePlan(accountId: string) {
    const invoice = await this.billingClient.fetchLatestInvoice(accountId);
    return {
      accountId,
      isPaid: invoice.status === "paid",
      plan: invoice.totalAmount > 0 ? "pro" : "free",
    };
  }
}

Architecture flow

Mermaid diagram rendering...

Clean Architecture Layer Structure

When looking at the DDD boundary from a Clean Architecture perspective, the structure of managing integrated policies between contexts at the application layer and isolating the infrastructure using adapters is stable.

LayerresponsibilityHow to apply
EntitiesContext Internal ModelBillingInvoice, ContentPost
Use CasesCross-context policyPublishIfPlanAllows
Interface AdaptersACL, Event TranslatorBillingAcl
FrameworksDB, message brokerPostgres, Kafka

Infrastructure diagram

Mermaid diagram rendering...

Tradeoffs

  • Clear boundaries make collaboration easier, but require initial design time.
  • Having an ACL can prevent contamination, but it increases the integration code.
  • Context separation is advantageous for expansion, but separation too early can result in over-design.

Cleanup

The goal of Bounded Context in startups is not a perfect microservice. Simply separating terminology and responsibilities and reducing boundary intrusions can significantly reduce future expansion costs.

Image source

  • Cover: source link
  • License: CC BY-SA 3.0 / Author: Eric Gaba (Sting - fr:Sting)
  • Note: After downloading the free license image from Wikimedia Commons, it was optimized to JPG at 1600px.

Comments