From User Stories to Entities - A Practical Map for Software Engineers

Shipping good software is not “just” about writing code. It’s about turning real user needs into clear models and predictable behaviors.

In most modern architectures (DDD, Clean Architecture, hexagonal, etc.), we keep seeing the same words:
user stories, features, use cases, entities, value objects, aggregates, repositories, DTOs, adapters, facades, domain events…

This article connects all of them in one coherent flow, using a simple e-commerce example, so any engineer can understand how the pieces fit.


1. The Big Picture

Here’s the high-level flow we’ll use:

User Story → Feature → Use Case → Domain Model (Entities, Value Objects, Aggregates) → Repository / Adapters / DTOs → UI via Facades → Domain Events

We’ll walk this path step by step.

Imagine a basic e-commerce: Shoply, an online store that sells shoes.


2. User Stories — Start with the Human

What it is
A user story is a short, human-centric description of a desired outcome, expressed from the user’s point of view.

Typical format

As a [type of user], I want [goal], so that [value].

Example (Shoply)

As a shopper, I want to add products to a cart so that I can review everything and check out once.

Why it matters

Key idea:
User stories are not technical. They’re about value.


3. Features — Product Slices You Can Ship

What it is
A feature is a coherent piece of functionality that delivers noticeable value to the user. It often maps to something they see in the product.

Examples (Shoply)

For our user story:

“As a shopper, I want to add products to a cart…”

We might define:

What a Feature includes

Role:
Features live mainly in product and UI discussions. They are how product managers and designers think about the system.


4. Use Cases — System Behaviors, Not Screens

What it is
A use case describes a specific behavior the system must perform to support a feature. It’s a clear, technical description of what happens when a user or external system triggers an action.

In DDD/Clean Architecture, Use Cases live in the Application Layer.

Examples (Shoply / Shopping Cart feature)

Responsibilities of a Use Case

Pseudo-code example

class AddItemToCartUseCase {
  constructor(private readonly cartRepository: CartRepository) {}

  async execute(input: {
    customerId: string;
    productId: string;
    quantity: number;
  }) {
    const cart = await this.cartRepository.getActiveCart(input.customerId);

    cart.addItem(input.productId, input.quantity);

    await this.cartRepository.save(cart);

    return cart.toDto();
  }
}

Key idea:
Features are “what the user sees.”
Use Cases are “what the system does.”


5. Entities — The Heart of the Business

What it is
An entity is a domain object with:

Entities live in the Domain Layer and represent the real business concepts.

Examples (Shoply)

Example: Cart Entity

class Cart {
  constructor(
    private readonly id: CartId,
    private readonly customerId: CustomerId,
    private items: CartItem[] = []
  ) {}

  addItem(productId: ProductId, quantity: number) {
    const existing = this.items.find((i) => i.productId.equals(productId));

    if (existing) {
      existing.increaseQuantity(quantity);
    } else {
      this.items.push(new CartItem(productId, quantity));
    }
  }

  // other behaviors: removeItem, updateQuantity, calculateTotal, etc.
}

Key idea:
Entities encapsulate rules and invariants, not just data.


6. Value Objects — Strong Types for Important Concepts

What it is
A value object is:

Examples (Shoply)

Example

class Money {
  constructor(
    readonly amount: number,
    readonly currency: "USD" | "EUR" | "BRL"
  ) {
    if (amount < 0) throw new Error("Amount cannot be negative");
  }

  add(other: Money): Money {
    if (this.currency !== other.currency) throw new Error("Currency mismatch");
    return new Money(this.amount + other.amount, this.currency);
  }
}

Relationship to Entities


7. Aggregates — Consistency Boundaries

What it is
An aggregate is a cluster of Entities and Value Objects that:

Examples (Shoply)

Why it matters

Example invariant:
“An order total must equal the sum of its items plus shipping minus discounts.”
→ This invariant is enforced inside the Order aggregate.


8. Repositories — Persistence Without Leaking the Database

What it is
A repository is an abstraction that provides methods to retrieve and save aggregates. It hides persistence details.

Examples (Shoply)

interface CartRepository {
  getActiveCart(customerId: string): Promise<Cart>;
  save(cart: Cart): Promise<void>;
}

Implementation might use:

…but the Use Case doesn’t care.

Relationship


9. DTOs — Crossing Boundaries Safely

What it is
A DTO (Data Transfer Object) is a plain data structure used to move data across boundaries:

DTOs are not domain models and shouldn’t contain business logic.

Example (Shoply)

interface AddItemToCartRequestDto {
  customerId: string;
  productId: string;
  quantity: number;
}

interface CartDto {
  id: string;
  customerId: string;
  items: {
    productId: string;
    quantity: number;
  }[];
  totalAmount: number;
  currency: string;
}

Why use DTOs


10. Adapters — Talking to the Outside World

What it is
An adapter is an implementation that connects your application to external systems:

In hexagonal/ports-and-adapters architecture:

Example (Shoply)

class HttpCartController {
  constructor(private readonly addItemToCartUseCase: AddItemToCartUseCase) {}

  async postAddItem(req, res) {
    const dto: AddItemToCartRequestDto = req.body;

    const cartDto = await this.addItemToCartUseCase.execute(dto);

    res.status(200).json(cartDto);
  }
}

Key idea:
Use Cases define what they need via ports (interfaces).
Adapters implement those ports with real technologies.


11. Facades — Making Life Easier for the UI

What it is
A facade is a thin service that:

Example (Shoply)

class CartFacade {
  constructor(
    private readonly addItemToCartUseCase: AddItemToCartUseCase,
    private readonly getActiveCartUseCase: GetActiveCartUseCase
  ) {}

  addItem(productId: string, quantity: number, customerId: string) {
    return this.addItemToCartUseCase.execute({
      productId,
      quantity,
      customerId,
    });
  }

  loadCart(customerId: string) {
    return this.getActiveCartUseCase.execute({ customerId });
  }
}

In the frontend, instead of wiring multiple use cases, components just call the facade.


12. Domain Events — Letting the System React and Scale

What it is
A domain event says: “Something important happened in the domain.”

Examples:

Example (Shoply)

When an order is placed:

class Order {
  // ...
  placeOrder() {
    if (!this.canPlace()) throw new Error("Order cannot be placed");

    this.status = "PLACED";

    this.domainEvents.push(
      new OrderPlacedEvent(this.id, this.customerId, this.total)
    );
  }
}

Why Domain Events are powerful

without modifying the original Use Case or Entity logic excessively.


13. How Everything Fits Together (End-to-End Flow)

Let’s walk through the story:

As a shopper, I want to add products to a cart so that I can review everything and check out once.

  1. User Story
    Defines the user’s goal and value.

  2. Feature: Shopping Cart
    PM and design create UX flows, errors, success states.

  3. Use Cases

    • AddItemToCartUseCase
    • GetActiveCartUseCase
  4. Entities & Aggregates

    • Cart (aggregate root)
    • CartItem (entity)
    • ProductId, CartId, Money, Quantity (value objects)
  5. Repositories

    • CartRepository to load and save Cart aggregates.
  6. Adapters

    • PostgresCartRepository implements CartRepository.
    • HttpCartController exposes /cart/items API.
  7. DTOs

    • AddItemToCartRequestDto from the HTTP body.
    • CartDto to return cart details to the frontend.
  8. Facades (Frontend)

    • CartFacade to let UI components call addItem and loadCart.
  9. Domain Events

    • Later, when CartCheckedOut occurs, we can:
      • Generate an Order.
      • Notify email service.
      • Update analytics.

Everything is connected, but each part has a clear responsibility.


14. Common Mistakes to Avoid

  1. Jumping straight to database design

    • Starting with tables instead of user stories and domain models.
    • Result: anemic entities, hard-to-change schemas.
  2. Putting business logic in controllers or UI

    • Logic should live in Use Cases and Entities, not random React components or HTTP handlers.
  3. Using entities as DTOs

    • Exposing internal domain objects directly over APIs.
    • Leads to tight coupling, versioning pain, and security issues.
  4. No clear aggregate boundaries

    • Putting too much in one entity (“God object”) or spreading invariants across multiple tables with no clear root.
  5. Skipping domain events

    • Hard to extend behavior without editing old code everywhere.

15. Final Takeaway

If you remember only one mental model from this article, make it this one:

User Story explains the value
Feature organizes the experience
Use Case defines the behavior
Entities / Aggregates / Value Objects encode the rules
Repositories / Adapters / DTOs / Facades handle communication & infrastructure
Domain Events let the system react and evolve.

When you structure your e-commerce (or any product) this way, you get: