Enterprise Architecture Patterns Tutorial 0/65 lessons ~6 min read Lesson 15

    Entities

    Entities are domain objects with identity that persists through state changes — distinguished by ID, not attribute values.

    Course progress0%
    Focus
    18 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Entities are domain objects with identity that persists through state changes — distinguished by ID, not attribute values. LinkedIn's Member, Connection, and JobPosting entities have lifecycles spanning years; architecture must track identity, authorization, and evolution without equating entities with database rows blindly.

    Real production story

    LinkedIn messaging refactored "Conversation" as a value object keyed by participant hash — when a member changed primary email, conversations forked and messages appeared lost. Root cause: identity was derived from mutable attributes instead of stable ConversationId assigned at creation. Re-modeling Conversation as entity with immutable ID, explicit merge rules for account linking, and event log of identity changes restored message threads. Member support tickets for "missing messages" dropped 70%; data migration backfilled IDs from audit log.

    Business problem

    LinkedIn graph entities live for decades with merges, splits, and privacy actions. Treating entities as dumb rows or value objects destroys identity continuity — member trust and regulatory erasure requests both fail.

    • Identity continuity: Account merge must combine graph entities without duplicating connections.
    • Compliance: GDPR erasure targets entity ID — must trace all child entities.
    • Evolution: Entity lifecycle states (active, dormant, restricted) drive different business rules.

    Architecture overview

    Entity equality by identity (ID), not attribute equality. Entities encapsulate lifecycle transitions with domain rules — member.restrict() not member.status = 'restricted' from controller.

    • Identity: Surrogate UUID/ULID assigned at creation — stable forever.
    • Lifecycle: State machine methods enforce valid transitions.
    • Inside aggregate: Some entities are internal to root — not referenced externally.
    • Persistence: ORM entity ≠ domain entity — map in repository layer.

    Architecture motivation

    Entities carry identity and lifecycle inside aggregates. Staff design distinguishes entity (has ID, mutable state) from value object (no ID, replaced wholesale) — confusion causes subtle production bugs.

    • Force: Long-lived professional graph with account linking and impersonation rules.
    • Constraint: Legacy schemas use natural keys — migrate to surrogate IDs with mapping table.
    • Outcome: Entity lifecycle documented; ID never derived from mutable fields.

    Internal architecture

    LinkedIn messaging entity model inside Conversation aggregate:

    • Message is entity inside Conversation — referenced by MessageId within thread only.
    • Member merge emits MemberMerged event — Conversation ACL updates participant refs.
    text
    Conversation (aggregate root)
    ├── ConversationId (entity identity — immutable)
    ├── ParticipantRefs (MemberId value refs)
    ├── Messages[] (Message entities — internal)
    │ └── MessageId, body, sentAt, editHistory
    └── lifecycle: Active · Archived · MemberErased
    Member (separate aggregate root)
    ├── MemberId
    ├── profile attributes (value objects)
    └── lifecycle: Active · Merged · Restricted · Deleted

    Data flow

    Entity creation: assign ID at birth → persist → emit Created event. Lifecycle: domain method validates transition → update → emit StateChanged event. Merge: identity mapping table records old→new IDs for async consumers.

    • Create message: Conversation.addMessage() creates Message entity with new MessageId.
    • Account merge: Member.mergeInto(target) reassigns foreign refs via event consumers.
    • Erasure: MemberDeleted triggers Conversation redaction — entity tombstone, not hard delete row scan.

    System design diagram

    Two diagrams show the Entities topology and the primary request/event path used in production at scale.

    Entities — system view
    Member entity
    Edge
    Connection entity
    Core
    Message thread
    Data
    Identity svc
    Async
    High-level topology for Entities.
    Entities — request / event flow
    Create
    Ingress
    Assign ID
    Store
    Lifecycle change
    Store
    Domain event
    Emit
    Follow this path when reviewing production designs.

    Production code example

    Member entity with lifecycle — Java domain entity LinkedIn-style:

    • ID field final — entity identity immutable even when profile value objects change.
    • Lifecycle methods return domain events for downstream identity mapping consumers.
    java
    public class Member {
    private final MemberId id;
    private MemberStatus status;
    private Profile profile;
    public Member(MemberId id, Profile profile) {
    this.id = Objects.requireNonNull(id);
    this.profile = profile;
    this.status = MemberStatus.ACTIVE;
    }
    public MemberMerged mergeInto(Member target) {
    if (this.status == MemberStatus.DELETED) {
    throw new IllegalStateException("Cannot merge deleted member");
    }
    if (!this.id.equals(target.id)) {
    this.status = MemberStatus.MERGED;
    return new MemberMerged(this.id, target.id, Instant.now());
    }
    throw new IllegalArgumentException("Cannot merge member into self");
    }
    public MemberRestricted restrict(RestrictionReason reason, Actor admin) {
    admin.requireRole(Role.TRUST_SAFETY);
    this.status = MemberStatus.RESTRICTED;
    return new MemberRestricted(this.id, reason, admin.id());
    }
    public MemberId id() { return id; } // identity never changes
    }

    Enterprise case study

    LinkedIn Conversation identity fix: Value-object identity caused forked threads on email change.

    • Before: Participant-hash key; 12k monthly "missing message" tickets.
    • Decision: Conversation entity with UUID; MemberMerged event; identity mapping service.
    • After: Tickets down 70%; merge completes in <5 min async; GDPR erasure traceable by ID.

    Trade-offs

    • Surrogate vs natural key: Natural keys (email) change — surrogate ID with lookup table.
    • Rich vs anemic entity: Rich domain methods vs service layer procedural — prefer rich for core entities.
    • Entity vs value object: Address as VO if no tracking needed; as entity if verification history matters.
    • Soft delete vs event erasure: Compliance may require crypto-shred — entity tombstone pattern.

    Security considerations

    Entity ID is authorization subject: Every mutation checks actor against entity ownership graph.

    • IDOR: Never expose sequential IDs — use opaque IDs and authz on every fetch.
    • Impersonation: Admin actions on Member entity require break-glass audit event.
    • Erasure: Entity deletion propagates as signed domain events — consumers must comply.

    Scalability analysis

    Billions of entities require shard-by-entity-ID, lazy lifecycle archival, and identity mapping service for merges at scale.

    • Shard key: MemberId hashes to partition — related entities co-locate when possible.
    • Archival: Dormant Conversation entities tier to cold storage — identity preserved.
    • Merge throughput: Account linking batch updates via event replay — not synchronous fan-out.

    Failure scenarios

    Entity identity failures: Duplicate IDs, forked identity on attribute change, merge without idempotency.

    • Forked thread: Natural key hash changed — assign immutable ConversationId at create.
    • Double merge: Retry merges same members — idempotent merge command on MemberId pair.
    • Orphan messages: Delete member without cascade plan — event-driven redaction pipeline.

    Staff engineer insights

    • If you compare entities with equals() on fields, you probably wanted a value object — entity equality is ID only.
    • Account merge is the stress test for entity design — if you haven't modeled merge, identity is incomplete.
    • ORM annotations on domain entities leak persistence — map in repository to keep lifecycle methods testable.

    Interview questions

    Interview Prep

    Practice concise answers, then expand each card for the explanation.

    3 questions
    1AdvancedQuestionEntity vs value object — decision criteria?+

    Answer

    Track individual thing through changes with same ID → entity. Describe attribute snapshot where replacement is OK → value object. Money, Address usually VO; Order, User usually entity. When in doubt: does business ask 'which one' (entity) or 'what value' (VO)?

    Follow-up

    Is DateRange entity or VO?
    2AdvancedQuestionDesign entity model for account merge in social graph.+

    Answer

    Member entity with mergeInto producing MemberMerged event with source and target IDs. Identity mapping service records aliases. Consumers update refs asynchronously. Idempotent merge. Tombstone source member entity — never reuse ID.

    Follow-up

    How handle messages from both pre-merge identities?
    3AdvancedQuestionShould domain entities have JPA annotations?+

    Answer

    No in clean DDD — persistence model separate; repository maps domain Member to JPA MemberJpaEntity. Prevents lazy-load leakage, schema coupling, and untestable entities. Exception: small teams may pragmatically colocate with strict module boundaries.

    Follow-up

    Cost of separate persistence model?

    Architecture review questions

    • Do entities have immutable surrogate identity assigned at creation?
    • Are lifecycle transitions enforced by domain methods with events?
    • Is entity equality based on ID only?
    • Are internal entities hidden — only root referenced externally?
    • Is account merge/linking modeled with identity mapping?
    • Is domain entity free of ORM/framework annotations?

    Summary

    Entities at LinkedIn model long-lived identity for members and conversations — stable IDs, rich lifecycle methods, and merge events — fixing forked threads and enabling compliant erasure across the professional graph.

    Ready to mark this lesson complete?Track your journey across the entire course.