Architecture Decisions
This section documents key architecture decisions made during design and development. Each Architecture Decision Record (ADR) captures the context, reasoning, and impact of a decision. Update this section when making significant architectural changes to maintain an accurate record of decisions shaping the system.
ADR 001: SDK Project Structure
Context
The Hedera Hashgraph DID SDK is designed to support decentralized identity (DID) features within the Hedera ecosystem. It consists of multiple components, such as Signer, Publisher or Resolver. The SDK is intended to be used by developers to build applications that leverage decentralized identity features on the Hedera network. Given this modular nature, we need to decide between organizing our project as a single monolithic package or as a collection of smaller, more focused packages.
Decision Drivers
-
Scalability: How well the repository structure will scale as the number of modules, contributors, and SDK languages grows.
-
Modularity: The ability to isolate specific components to reduce complexity and ensure independent release cycles.
-
Developer Experience: The ease of contribution, debugging, and reviewing code for both current and future developers.
-
CI/CD and Automation: The ability to manage continuous integration, testing, and deployment pipelines efficiently.
-
Dependency Management: How the structure impacts managing shared dependencies and versioning.
Considered Options
Option 1: Single library Approach
Description: A single library containing all components and SDKs for the Hedera DID system.
Pros:
-
Cross-Module Integration Testing: Having all components in one place allows for seamless integration tests across different modules, ensuring compatibility between underlying components.
Cons:
-
Lack of Flexible Release: If one component needs to be released independently, it might be challenging to decouple its release process from the rest of the repository.
-
Merge Conflicts: With multiple teams working on the same library, the likelihood of merge conflicts increases, especially if there are many simultaneous feature branches.
-
Increased Build and Test Times: Even if a small part of the codebase changes, the entire repository might need to be built and tested. This can slow down development cycles, especially as more modules are added.
-
Repository Size and Complexity: As the number components grows, the repository may become large and complex, making it harder to navigate and manage. Cloning, searching, and building may become slower over time.
Option 2: Multiple libraries Approach
Description: Multiple libraries, where each component has its own library.
Pros:
-
Independent Release Cycles: Each library can follow its own release schedule, which is beneficial when some libraries evolve faster than others.
-
Easier Repository Navigation: With smaller libraries, developers and contributors can easily navigate the codebase relevant to their task, making the development process more manageable for newcomers.
-
Reduced Risk of Merge Conflicts: Since each library is managed separately, developers working in different repositories will not face merge conflicts due to changes in unrelated codebases.
-
Use What You Need: Developers can choose to use only the libraries they need, reducing the cognitive load and dependencies for projects that do not require the full suite of components.
Cons:
-
Potential Duplication: Similar functionality may be duplicated across libraries, especially when different teams manage different components, leading to inconsistencies and redundant work.
Decision
After careful consideration, the decision is to adopt the multiple libraries approach. This decision is based on the following rationale:
-
Modularity: The multiple libraries approach aligns with the modular design of the SDK, allowing for independent development and release cycles for each component.
-
Scalability: As the number of components grows, the multiple libraries approach will help maintain a manageable codebase and facilitate easier navigation for developers.
-
Developer Experience: By isolating components into separate libraries, we can improve the developer experience by reducing the cognitive load and making it easier to contribute to specific areas of the SDK.
-
CI/CD and Automation: The multiple libraries approach will enable more efficient CI/CD pipelines, as changes in one library will not trigger unnecessary builds and tests for unrelated components.
ADR 002: SDK Repository Tooling
Context
he project is structured as a monorepo, managing multiple libraries for decentralized identity (DID) functionality on the Hedera network. The monorepo includes various packages, such as libraries for cryptographic operations, and DID document management. To effectively manage the monorepo, we need a tool that can handle versioning, package management, building, and testing across multiple JavaScript and TypeScript packages. The decision is to choose the appropriate tooling to support the development and maintenance of this monorepo, with options including Lerna, Nx, Turborepo and Yarn Workspaces.
Decision Drivers
-
Management: The ability to manage multiple JavaScript/TypeScript packages efficiently within the same repository.
-
Versioning: Tools that support automatic versioning and changelog generation across multiple packages.
-
Performance: Handling dependencies between packages and optimizing build times.
-
Testing: The ability to run independently unit, integration, and end-to-end tests across packages, and to ensure that all necessary dependencies are built and tested correctly.
-
Developer Experience: Ease of setup and use for developers working on different packages within the monorepo.
-
Community Support: The availability of documentation, community support, and active development for the chosen tooling.
-
Simplicity: The tool should be easy to understand and integrate into the existing development workflow.
Considered Options
Option 1: Lerna
Description: Lerna is a popular tool for managing JavaScript monorepos. It simplifies workflows around managing multiple packages in a single repository, supporting independent or locked versioning, dependency hoisting, and linking of local packages.
Pros:
-
Well-established: Lerna has been widely adopted, with strong community support and a rich ecosystem of plugins and extensions.
-
Dependency Hoisting: Shared dependencies can be hoisted to the root level of the monorepo, minimizing duplication and ensuring consistency across packages.
-
Independent Versioning: Supports both independent versioning of packages and fixed (locked) versioning for more controlled releases.
-
Automatic Package Linking: Local packages are automatically linked, which makes cross-package development straightforward without manually managing npm link.
-
Automated Release Workflow: Lerna provides tools for automated release management, including changelog generation and publishing to npm.
Cons:
-
No Built-in Build Optimization: Lerna focuses primarily on versioning and managing packages. It does not include build caching or task orchestration, which are important for optimizing large monorepos.
-
Scaling Limitations: As the number of packages grows, Lerna’s performance can degrade without additional tools for build performance.
-
Requires Additional Tools: Lerna doesn’t provide advanced task running or build optimization, so additional scripts or tools (e.g., Webpack, Babel) are often needed.
Option 2: Nx
Description: Nx is a monorepo management tool that focuses on optimizing build performance and developer productivity. It provides features for caching, distributed builds, and dependency-aware task execution, with a built-in support for JavaScript/TypeScript, as well as other languages and technologies.
Pros:
-
Advanced Build Caching: Nx caches the results of previous builds and only rebuilds packages when necessary, leading to significant performance improvements.
-
Dependency Graph Visualization: Nx automatically generates a dependency graph that shows how different packages interact, helping developers manage dependencies effectively.
-
Task Orchestration: Nx intelligently runs build, test, and lint tasks in the correct order, based on package dependencies, and only when needed.
-
Language Agnostic: Nx supports not only JavaScript/TypeScript but also other languages, which is helpful if the monorepo eventually incorporates non-JS packages (e.g., Rust, Go).
-
Scalable for Large Monorepos: Nx is designed to handle the needs of large monorepos, making it suitable for complex projects with many interdependent packages.
Cons:
-
Learning Curve: Nx has a more complex setup and configuration than simpler tools like Lerna, especially around caching and task orchestration.
-
Overhead: For smaller projects, Nx’s comprehensive features may introduce unnecessary complexity.
-
Complexity: As the monorepo grows, managing Nx’s configuration files and workflows for different environments and tools can become complicated.
Option 3: Turborepo
Description: Turborepo is a high-performance monorepo build system focused on reducing build times with its built-in caching and task scheduling. It is a more recent tool with a strong emphasis on speed and developer experience.
Pros:
-
Fast Build Caching: Turborepo offers a robust caching system that prevents rebuilding or retesting packages unless absolutely necessary, improving build performance.
-
Minimal Setup: Turborepo integrates smoothly with existing build systems and requires minimal configuration, making it easy to adopt.
-
Task Orchestration: Turborepo runs tasks in parallel and in the correct order based on package dependencies, ensuring fast and correct builds and tests.
-
Modern Tooling Integration: Turborepo works well with other modern JavaScript/TypeScript tools like Webpack, Babel, and TypeScript, providing a good balance of flexibility and performance.
Cons:
-
Newer Tool: As a newer tool, Turborepo does not have the same level of community support and ecosystem maturity as Lerna or Nx.
-
Less Comprehensive Feature Set: Turborepo is primarily focused on build performance and doesn’t offer many project management features (e.g., dependency graphs, testing utilities).
-
Focused on JavaScript/TypeScript: Turborepo is optimized for JavaScript/TypeScript projects, which could limit its applicability if non-JS packages are added to the monorepo in the future.
-
Lack of Built-in Testing and Linting: Unlike Nx, Turborepo does not come with built-in support for testing or linting, requiring additional setup for those workflows.
Option 4: Yarn Workspaces
Description: Yarn Workspaces is a feature provided by Yarn, a popular package manager, to manage multiple packages in a monorepo. It allows you to link packages together and install dependencies in a centralized way.
Pros:
-
Built-in Package Linking: Yarn Workspaces automatically links local packages, making it easy to develop multiple interdependent packages without needing external tools.
-
Simplicity: Yarn Workspaces is relatively simple to set up and integrates directly with Yarn, requiring no additional tooling beyond a package.json configuration.
-
Centralized Dependency Management: Dependencies are installed once at the root level and shared among all packages, reducing redundancy and potential version conflicts.
-
Lightweight Solution: For smaller monorepos, Yarn Workspaces offers an elegant and lightweight approach to managing multiple packages without the overhead of more complex tools like Nx or Lerna.
-
Minimal Configuration: Yarn Workspaces is configured directly in the package.json file, which simplifies repository management without needing additional configuration files.
Cons:
-
No Built-in Task Runner: Yarn Workspaces only handles package linking and dependency management. It doesn’t provide a task runner for building, testing, or linting across packages.
-
No Advanced Build Caching: Unlike Nx or Turborepo, Yarn Workspaces does not provide advanced build caching or task orchestration, which can lead to longer build times as the monorepo grows.
-
Requires Additional Tooling: To handle tasks like building, testing, and deployment, additional scripts or tools (e.g., npm/yarn scripts, Webpack) are required.
-
Limited Dependency Management: Yarn Workspaces lacks advanced features like dependency graph visualization or fine-grained control over package relationships, making it harder to manage complex interdependencies in larger monorepos.
Decision
The decision is to use Lerna as the primary tooling for managing the Hedera Hashgraph DID SDK monorepo. Reasons for this decision include:
-
Simplicity and Familiarity: Lerna is easy to use and has a minimal learning curve, making it a good choice for the team’s current needs. It doesn’t require complex setup or configuration.
-
Package Management and Versioning: Lerna’s ability to manage interdependent packages, handle versioning, and automate releases will streamline development and reduce manual overhead.
-
Community Support: Lerna is a mature tool with strong community support, ensuring long-term maintainability and stability.
-
No Overhead for Build Optimization: Since the current monorepo is small-sized, the lack of build caching or task orchestration in Lerna is not a significant issue at this stage.
Follow-Ups
-
Review and Adjust: Review this ADR one month post-implementation to compare expectations with actual outcomes.
-
Document Further ADRs: Document any additional ADRs arising from this decision, especially for further adjustments or enhancements.
ADR 003: Monorepo Project Structure
Context
This ADR defines the high-level project structure for the Hedera Hashgraph DID SDK monorepo. A well-defined structure is crucial for maintainability and scalability as the project grows.
Decision
The monorepo will be organized into the following top-level directories:
-
packages/
: Contains individual packages for different parts of the SDK (e.g., core DID library, signers, utilities). -
examples/
: Includes sample applications and code snippets demonstrating SDK usage. -
docs/
: Contains documentation files, guides, and API references. -
scripts/
: Includes scripts for common tasks (building, testing, linting, releasing). -
test-suites/
: Contains test files and configurations for BDD tests.
Consequences
-
Improved Maintainability: A clear structure makes it easier to maintain the codebase.
-
Enhanced Scalability: Allows for adding new packages without increasing complexity.
ADR 004: Package Structure and Conventions
Context
This ADR defines the structure and conventions for individual packages within the Hedera Hashgraph DID SDK monorepo.
Decision
Each package within the monorepo will follow this structure:
-
src/
: Contains the TypeScript source code. -
tests/
: Includes test files (unit, integration, end-to-end).
Each package must include these files:
-
package.json
: Defines package metadata, dependencies, scripts, and configurations. -
tsconfig.json
: Specifies TypeScript compiler options. -
jest.config.ts
: Configures Jest for testing and code coverage. -
README.md
: Provides package information.
ADR 005: SDK Packages and External Dependencies
Context
This ADR provides an overview of the core packages within the Hedera Hashgraph DID SDK and outlines the key external dependencies required by the SDK.
Decision
The Hedera Hashgraph DID SDK will be composed of the following core packages:
-
core
: Provides interfaces and utilities for other SDK packages. It’s a required dependency for all other packages. -
registrar
: Implements the DID registration process and operations like updating and deactivating DIDs on the Hedera network. -
resolver
: Allows resolving DID documents from the Hedera network and verifying their integrity. -
signer-internal
: Implements an internal signer that uses a local key pair (Hedera Private Key Class) to sign messages. -
publisher-internal
: Implements an internal publisher (using Hedera Client) that publishes Hedera transactions to the network. -
messages
: Defines the message formats for Hedera DID messages and lifecycle events for them and lifecycle manager.
The SDK will utilize the following external dependencies:
-
@hashgraph/sdk
: The official Hedera SDK for interacting with the Hedera network, sending transactions, and querying state.
Consequences
-
Modularity and Reusability: Separating functionality into distinct packages promotes modularity and allows for easier reuse of components within the SDK and potentially in other projects.
-
Maintainability: A clear division of responsibilities across packages simplifies maintenance and updates.
-
Flexibility: The modular design allows developers to select and use only the packages necessary for their specific needs.
-
Extensibility: The package structure enables easier extension of existing functionality and the addition of new features.
ADR 006: Relocation of the DID-SDK BDD Test Suite
Context
The DID-SDK BDD test suite, designed for language-agnostic validation of the DID-SDK, is currently non-functional and tightly coupled with the main SDK repository. This coupling hinders independent development, dedicated maintenance, and efficient resource allocation, necessitating a strategic decision on its future.
Decision Drivers
-
Independent Development & Release: Enable separate development and release cycles, decoupled from the main SDK.
-
Focused Resource Allocation: Allow dedicated resources with specialized BDD and testing expertise.
-
Maintainability & Scalability: Significantly improve the test suite’s maintainability and long-term scalability.
-
Clarity of Purpose: Establish the test suite as a well-defined, standalone tool.
Considered Options
Option 1: Maintain within Current Repository
-
Description: Continue maintaining the BDD test suite within the existing DID-SDK repository.
-
Pros: Code proximity; no new repository setup required.
-
Cons: Competition for resources; unclear separation of concerns; impedes independent maintenance; tightly coupled CI/CD; necessitates mixed skillsets.
Option 2: Relocate to Separate Repository
-
Description: Migrate the BDD test suite to a dedicated, independent repository.
-
Pros: Enables independent development and release cycles; facilitates dedicated resource allocation; promotes improved maintainability and organization; establishes clear ownership; increases potential for reuse by other projects; allows for an independent, optimized CI/CD pipeline.
-
Cons: Introduces repository management overhead; requires careful synchronization with SDK changes; necessitates an initial migration effort.
Option 3: Discard the Test Suite
-
Description: Abandon the current BDD test suite and explore alternative testing approaches.
-
Pros: Avoids further investment in the current, flawed implementation; allows exclusive focus on core SDK development.
-
Cons: Loss of the potential benefits of a language-agnostic BDD test suite; increased risk of regressions without a comprehensive alternative; forfeits the initial investment in the BDD suite.
Rationale
-
Strategic Alignment: Directly supports the original goal of a language-agnostic testing tool by enabling focused, independent development.
-
Enhanced Maintainability: A separate repository fosters better code organization, reducing complexity and facilitating ongoing maintenance.
-
Optimized Resource Utilization: Enables the allocation of specialized resources with the necessary BDD and testing expertise.
-
Long-Term Viability: Establishes a sustainable foundation for the test suite’s long-term growth and scalability.
-
Expanded Utility: Positions the test suite as a standalone tool, potentially usable for testing other components or projects.
Follow-Ups
-
Create New Repository: Establish the new repository with appropriate access controls.
-
Migration Plan: Develop a detailed plan for migrating code, configurations, and associated resources, minimizing disruption.
-
Documentation: Prioritize creating comprehensive documentation, including setup, contribution guidelines, and test execution instructions.
-
Roadmap: Define a clear roadmap for addressing identified weaknesses, implementing improvements, and expanding test coverage.
-
Resource Allocation: Assign dedicated resources with the required expertise to the new repository.
-
Communication: Communicate the decision, relocation plan, and rationale to all relevant stakeholders.
-
Review and Adjust: Review the effectiveness of this ADR and the relocated test suite three months post-implementation.