You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The current EquipChain codebase has data access logic mixed directly into route handlers and services, leading to tight coupling between business logic and data storage mechanisms. This refactoring introduces the Repository Pattern to abstract data access behind clean interfaces, making the codebase more maintainable, testable, and adaptable to different data storage backends (in-memory, database, external API).
The Repository Pattern involves creating repository classes for each domain entity that encapsulate all data access operations (CRUD, query, aggregation). Route handlers and services will depend on these repository interfaces rather than directly manipulating data structures. This abstraction enables: swapping storage backends without changing business logic (e.g., migrating from in-memory to PostgreSQL), simplifying unit tests by mocking repositories, centralizing data access validation and error handling, and providing a single place to add caching, logging, or audit trails for data operations.
Initial repositories to create: UserRepository (admin users), DeviceRepository (registered meters/devices), MeterReadingRepository (reading data points), WebhookRepository (webhook registrations and delivery logs), ApiKeyRepository (API key store), and ConfigRepository (protocol configuration). Each repository should follow a consistent interface: findById(id), findAll(query), create(data), update(id, data), delete(id). Additional domain-specific methods should be added as needed (e.g., findByPublicKey(publicKey) for users, findByMeterId(meterId, dateRange) for readings).
Architecture: New src/repositories/ directory with one file per entity. Each repository exports a class implementing a documented interface. Current in-memory stores will be migrated from service files into these repositories. Service files (auth, analytics, webhooks) will be updated to use repositories instead of direct data manipulation.
Impact: This is a foundational refactoring. It affects almost every service and route handler. The immediate benefit is testability (repositories can be mocked). Long-term, it enables smooth migration to a database without rewriting business logic.
Step-by-Step Implementation Guide
Define Repository Interface: Create src/repositories/base.js (or a JSDoc type definition) documenting the standard repository API: findById, findAll, create, update, delete. Include TypeScript-style JSDoc comments for IDE autocompletion.
Create In-Memory Repository Base: Write src/repositories/BaseRepository.js implementing the common interface using an in-memory Map. Include built-in filtering, pagination (reusing Issue [Feature] Create Pagination, Filtering, and Search Utilities for All List Endpoints #17), and event emission for data changes. Other repositories will extend this base class.
Implement Domain Repositories: Create UserRepository.js, DeviceRepository.js, MeterReadingRepository.js, WebhookRepository.js, ApiKeyRepository.js, ConfigRepository.js. Each extends BaseRepository, adds domain-specific methods, and pre-seeds any default data (e.g., default config values, initial admin user).
Migrate Data from Services: Move in-memory stores from service files (auth.js, webhook.js, apiKey.js, etc.) into the appropriate repositories. Update service files to import and use repositories instead of maintaining their own data stores.
Update Route Handlers: Refactor route handlers to use repositories. For example, admin/users.js should call userRepository.findAll() instead of directly accessing an array.
Update Tests: Refactor tests to use repositories with mock data. Create tests/unit/repositories/ with tests for each repository verifying CRUD operations, filtering, and edge cases.
Write Integration Test: Create a test that verifies the full flow from route → service → repository with the in-memory backend.
Verification & Testing Steps
Run npm test — verify existing tests still pass after the refactoring. Some tests may need minor updates due to changed import paths.
Verify each repository's CRUD operations work correctly by running tests/unit/repositories/*.test.js.
Start the server and verify all API endpoints still function correctly — create a user, list devices, register webhooks, etc.
Create a mock repository for testing (e.g., class MockUserRepository extends BaseRepository) and verify it integrates cleanly with the service layer.
Verify that the in-memory store is no longer accessed directly from any service or route handler — grep for direct Map or Array usage in service files to confirm migration is complete.
Description
The current EquipChain codebase has data access logic mixed directly into route handlers and services, leading to tight coupling between business logic and data storage mechanisms. This refactoring introduces the Repository Pattern to abstract data access behind clean interfaces, making the codebase more maintainable, testable, and adaptable to different data storage backends (in-memory, database, external API).
The Repository Pattern involves creating repository classes for each domain entity that encapsulate all data access operations (CRUD, query, aggregation). Route handlers and services will depend on these repository interfaces rather than directly manipulating data structures. This abstraction enables: swapping storage backends without changing business logic (e.g., migrating from in-memory to PostgreSQL), simplifying unit tests by mocking repositories, centralizing data access validation and error handling, and providing a single place to add caching, logging, or audit trails for data operations.
Initial repositories to create:
UserRepository(admin users),DeviceRepository(registered meters/devices),MeterReadingRepository(reading data points),WebhookRepository(webhook registrations and delivery logs),ApiKeyRepository(API key store), andConfigRepository(protocol configuration). Each repository should follow a consistent interface:findById(id),findAll(query),create(data),update(id, data),delete(id). Additional domain-specific methods should be added as needed (e.g.,findByPublicKey(publicKey)for users,findByMeterId(meterId, dateRange)for readings).Technical Context & Impact
src/repositories/directory with one file per entity. Each repository exports a class implementing a documented interface. Current in-memory stores will be migrated from service files into these repositories. Service files (auth, analytics, webhooks) will be updated to use repositories instead of direct data manipulation.Step-by-Step Implementation Guide
src/repositories/base.js(or a JSDoc type definition) documenting the standard repository API:findById,findAll,create,update,delete. Include TypeScript-style JSDoc comments for IDE autocompletion.src/repositories/BaseRepository.jsimplementing the common interface using an in-memory Map. Include built-in filtering, pagination (reusing Issue [Feature] Create Pagination, Filtering, and Search Utilities for All List Endpoints #17), and event emission for data changes. Other repositories will extend this base class.UserRepository.js,DeviceRepository.js,MeterReadingRepository.js,WebhookRepository.js,ApiKeyRepository.js,ConfigRepository.js. Each extends BaseRepository, adds domain-specific methods, and pre-seeds any default data (e.g., default config values, initial admin user).auth.js,webhook.js,apiKey.js, etc.) into the appropriate repositories. Update service files to import and use repositories instead of maintaining their own data stores.admin/users.jsshould calluserRepository.findAll()instead of directly accessing an array.tests/unit/repositories/with tests for each repository verifying CRUD operations, filtering, and edge cases.Verification & Testing Steps
npm test— verify existing tests still pass after the refactoring. Some tests may need minor updates due to changed import paths.tests/unit/repositories/*.test.js.class MockUserRepository extends BaseRepository) and verify it integrates cleanly with the service layer.