This EIP introduces a domain-based architectural pattern for contracts implementing the Diamond execution model defined by ERC-2535 (Diamond Standard) or ERC-8109 (Diamond, Simplified), together with the storage identifier mechanism defined by ERC-8042 (Diamond Storage Identifier).
It defines a consistent naming convention for storage identifiers and a directory organization model that decouples storage management from facet logic.
This pattern helps reduce storage collisions and human error while enabling better tooling for multi-facet systems.
ERC-2535 provides a flexible foundation for modular smart contracts through facets, but it intentionally leaves storage organization and architectural conventions open to implementation.
While this flexibility encourages creativity, it can sometimes lead to inconsistency.
Each developer or team may structure storage differently, making it harder to design robust and easy-to-use tooling for storage management.
Without a shared structural framework, storage identifiers may be inconsistently verified or reused across facets, which can result in unexpected collisions or subtle upgrade issues between facets sharing the same state.
ERC-8042 introduced human-readable storage identifiers to improve clarity, but it does not define how those identifiers should be structured or grouped in larger projects.
This EIP proposes a domain-centric architectural pattern that establishes a consistent framework for managing storage independently of facet implementation.
By introducing clear domain boundaries and deterministic naming rules for storage identifiers, the pattern maintains the openness of the Diamond Standard while providing a shared foundation for collaboration, tooling support, and long-term upgrade safety across complex systems.
A domain represents the conceptual ownership of a storage space.
Each domain corresponds to exactly one storage struct and one identifier.
A domain has a one-to-many relationship with the group of function selectors that access it.
A domain is independent of facets, multiple facets MAY read or modify the same domain.
Domains SHOULD be defined according to business or system responsibility, not by facet name.
A storage identifier is the human-readable string whose keccak256 hash defines a Diamond Storage position.
It represents the domain that owns and manages a specific storage layout.
To ensure uniqueness and clarity, at a minimum, a storage identifier SHOULD include the following components:
To improve readability, namespace separation, and tooling support, additional contextual components MAY be included, resulting in the following extended format:
org
Optional organization or author prefix (e.g., eth, vag, safe)
project
Project or protocol name
domain_type
Optional classification of the domain. If present, it SHOULD be one of:
diamond — core Diamond protocol domains, such as upgrade, introspection, and ownership.
system — shared system-level domains providing cross-cutting functionality. (e.g., reentrancy, pause, access control)
business — application-specific domains. (tokens, guards, modules)
domain_name
Lowercase keyword identifying the storage domain
version
Optional storage layout version identifier
The initial storage layout is conceptually treated as v1.
If omitted, the identifier refers to this initial (v1) storage layout for backward compatibility.
v2, v3, … MUST be used for layout-breaking changes.
The version MUST be incremented only when the storage layout is no longer append-only.
Each domain:
MUST have one storage struct and one identifier.
SHOULD be implemented in a dedicated directory named after the domain.
If new fields are added to a storage struct, they MUST be added only at the end, and the struct MUST remain append-only.
A sub-domain represents a storage-isolated vertical extension of an existing domain.
A sub-domain is used when new functionality belongs conceptually to an existing domain, but its required state can be cleanly isolated without modifying or appending to the original domain’s storage layout.
If present, the sub-domain's identifier format becomes:
or, when using the extended format:
The version component indicates the domain version in which the sub-domain was introduced.
It serves as a historical and organizational reference, not as an independent versioning lifecycle for the sub-domain.
Definition and Rules
A sub-domain:
MUST define its own ERC-8042 storage identifier.
MUST define its own storage layout
MUST NOT modify or append to the parent domain’s storage.
MUST remain conceptually subordinate to the parent domain.
MUST share the same version context as the parent domain.
SHOULD be placed alongside the parent domain’s storage definitions.
Sub-domains exist as a safety-oriented design choice to isolate newly introduced state, while preserving the original domain layout unchanged.
Choosing between evolving the existing domain storage or introducing a sub-domain depends on the project’s complexity, the team’s discipline, and long-term maintenance goals.
To support reliable tooling and explicit storage ownership, each domain defined by this proposal MUST declare its storage location using the ERC-8042 NatSpec annotation.
Specifically, the domain-owned storage struct MUST be annotated with:
This proposal does not redefine the storage location formula, but requires the use of this annotation to ensure that domain storage is discoverable, unambiguous, and machine-readable.
The extended identifier format is recommended for global uniqueness. It is especially useful when integrating shared libraries, predefined facets, or other standards, where namespace collisions are more likely.
For application-specific systems, teams may choose a minimal identifier format to reduce naming complexity, as long as the identifier remains stable and unique within the project.
Equipment Identifier
Represents a business domain responsible for equipment state. This domain has undergone a layout-breaking change, therefore uses an explicit v2 identifier.
Character Identifier
Represents a business domain responsible for character state and progression. This example also demonstrates how a domain can be extended using a storage-isolated sub-domain.
Game Setting Identifier
Defines a system-level domain for game-wide configuration shared across multiple facets.
In line with Domain-Driven Design principles, the directory layout SHOULD reflect domain ownership.
Each domain defines a logical namespace for storage ownership.
Directories are named after this namespace and serve as its physical representation in the codebase.
Facets act as logic containers and do not own storage.
They MAY reside alongside domain directories or reference domain-owned logic.
Both directory names and storage identifiers SHOULD include domain information.
This consistency allows tooling and precompilers to automatically associate selectors, domains, and storage layouts.
Each domain SHOULD be represented by a dedicated directory.
Within this directory, domain-owned logic such as storage layout definitions, internal helper logic, and any facets primarily associated with the domain MAY be organized under subdirectories as needed.
This structure reduces the risk of storage collisions by design.
The alignment of domain namespaces, directory layout, and storage identifiers allows file system constraints and static analysis tools to surface conflicts early and reason about upgrades proactively.
This directory structure is illustrative and does not mandate a specific naming convention.
Subdirectory names such as storage/ are illustrative and may contain both storage layout definitions and internal domain logic.
This architecture defines upgrade behavior based on the effect new selectors introduce to domains and storage, rather than on facets themselves.
Upgrades fall into one of the following cases.
Case 1: No new storage required
If new selectors do not require any additional storage:
This is the simplest and safest upgrade path, as it introduces no new state and does not affect existing storage layouts.
Case 2: New domain required (horizontal upgrade)
If new functionality introduces state that does not logically belong to any existing domain:
This represents a horizontal expansion of the system, allowing new features to be introduced without impacting existing domains or storage layouts.
Case 3: New variables within an existing domain (vertical upgrade)
If new selectors require additional state that logically belongs to an existing domain, and the existing storage layout is not broken, this becomes a design trade-off.
Two common approaches MAY be used:
Option A — Evolve the existing domain
This keeps the domain unified and works well for tightly coupled or complex business logic.
It requires strict discipline when managing storage layout.
Option B — Introduce a sub-domain
This approach reduces risk and cognitive load by isolating newly introduced state, while keeping the original domain layout stable.
The choice between these approaches depends on project complexity, team discipline, and long-term maintenance goals.
Case 4: Layout-breaking change
If new selectors require a change that breaks the existing storage layout of a domain
(for example, changing the inner structure of nested structs or struct arrays)
This architecture does not attempt to automate or abstract storage migration.
The goal is to keep schema changes intentional, visible, and auditable.
If the layout-breaking change is partial, and the newly required state can be cleanly isolated and defined independently, developers MAY also consider introducing a sub-domain instead of versioning the entire domain.
From the beginning, the Diamond Standard (ERC-2535) was designed around the relationship between function selectors and storage positions, not around facets themselves.
Facets are replaceable units of logic — the diamondCut operation only replaces, removes or adds code — but the storage layout persists and defines the actual state continuity of the contract.
A clear example of this can be found in Reference Implementation of ERC-8109.
Both DiamondUpgradeFacet and DiamondInspectFacet interact with the same storage.
Although these facets serve different purposes — one mutating, one querying — they share the same domain (erc8109.diamond).
This demonstrates that storage belongs to the domain, not the facet, facets merely provide interfaces for logic to read or mutate that domain.
Over time, many implementations have treated facets as the primary boundary of responsibility, grouping logic and storage together without recognizing that storage domains are the true architectural anchors.
This misunderstanding leads to inconsistent storage management, overlapping identifiers and fragile upgrade paths where one facet unintentionally corrupts another’s state.
The domain-centric approach restores the original intent of the Diamond:
Selectors (facets) operate through domains, not as domains.
Each domain defines its own persistent storage struct and identifier, while facets merely act as interfaces that execute logic against it.
This shift decouples storage from logic when separation is desired, while still allowing tightly coupled designs when intentional.
It enables:
By formalizing this pattern, Diamond Architecture becomes safer, more transparent and easier to extend — re-aligning practice with its original design philosophy.
There is a special case within this separation principle where a domain and its facet intentionally represent the same entity.
A great example of this approach can be found in the Compose project
In Compose, a facet and its associated domain are explicitly mapped into a single entity.
This is a deliberate design choice that enables predefined, plug-and-play standard facets with a well-defined storage layout and reduced collision risk.
This approach is suitable for systems that prioritize modular composition and standardized functionality, allowing developers to safely integrate common features with predictable behavior.
However, when implementing custom or project-specific logic, domains and facets SHOULD still be treated as separate entities.
Maintaining this separation preserves clarity of ownership, supports future upgrades, and improves the long-term scalability of complex Diamond-based architectures.
Sub-domains can also serve as a practical way to isolate layout-sensitive state.
Projects that need to move quickly may choose to place complex or layout-unstable data (such as mappings or dynamic arrays) in a primary domain, while isolating smaller or more compact state in sub-domains.
As development progresses, additional state can either be appended to an existing domain or introduced via a sub-domain, depending on data shape and evolution needs.
This allows projects to start with a simple structure while preserving flexibility to refine storage organization as the system scales.
This proposal is fully backward-compatible with ERC-2535 (Diamond Standard), ERC-8042 (Diamond Storage Identifier) and ERC-8109 (Diamond, Simplified). It introduces no breaking changes, no new opcodes, and no modifications to existing protocol mechanics.
It does not alter the execution model defined by ERC-2535 or ERC-8109. The relationships between facets, selectors, and shared storage remain unchanged and fully compatible across all three standards.
Instead, this proposal defines an architectural convention that complements existing Diamond standards by:
This architecture may be applied to contracts implementing either ERC-2535 or ERC-8109, as both share the same fundamental facet and selector architecture.
Developers are encouraged to continue following all applicable standards to maintain interoperability while benefiting from clearer state ownership, reduced storage collision risk, and lower architectural complexity.
For already deployed systems, adoption can be done incrementally.
The Domain Architecture does not require projects to modify, rename, or refactor existing libraries or other standards in order to adopt it. If a library or standard already follows ERC-2535, ERC-8042, or uses its own established storage identifiers, that code SHOULD remain unchanged.
Adoption MAY begin at the application layer, without touching shared libraries or standardized components. Existing storage identifiers MAY be treated conceptually as pre-v1 domains.
In practice, projects typically start by:
@custom:storage-location annotationAdopting the Domain Architecture does not require migrating existing state. It primarily affects how new storage is introduced and how future upgrades are structured.
When a layout-breaking change is required, a new versioned storage identifier can be introduced explicitly, allowing existing storage layouts to remain untouched. Any data migration, if needed, MUST be handled explicitly by the project.
The primary consideration during adoption is identifier uniqueness. New storage identifiers MUST NOT collide with existing identifiers from shared libraries or from within the project itself.
Minimal implementation examples demonstrating the convention:
Business Domain (Equipment)
Business Domain (Character)
System Domain (Game Settings)
Each domain defines and owns its storage independently. Facets interact with domain-owned storage definitions, supporting safe upgrades and avoiding unintended storage overlap.
This pattern strengthens the security model of Diamond-based systems by introducing explicit and deterministic storage identifiers.
By separating domains and enforcing consistent naming rules, it reduces the risk of:
Each domain owns its ERC-8042 storage identifier. When combined with append-only storage layout upgrades, this allows storage evolution without interfering with existing state.
This clarity also improves auditability and supports static analysis tooling when analyzing storage safety across upgrades.
Copyright and related rights waived via CC0.