Repository Layer Architecture
The ROAR backend implements a sophisticated repository pattern that provides a flexible and maintainable approach to data access. This architecture is illustrated through three key structural layers:
1. Base Repository Interface Layer
The foundation of our repository pattern is the generic BaseRepository<T>
interface, which defines standard CRUD operations:
- create()
- get()
- getAll()
- update()
- delete()
- runTransaction()
This interface ensures consistent data access patterns across all entity types while maintaining type safety through generics.
2. Specialized Repository Interfaces
Building on the base interface, we define specialized repository interfaces for specific entity types:
- AdministrationBaseRepository
- UserBaseRepository
- UserClaimBaseRepository
- IdentityProviderBaseRepository
- OrgBaseRepository
Each specialized interface extends BaseRepository and adds entity-specific operations where needed. For example:
- AdministrationBaseRepository adds
getByName()
- UserClaimBaseRepository adds
getByRoarUid()
- IdentityProviderBaseRepository adds
getByProviderId()
3. Concrete Implementation Layer
These concrete implementations encapsulate the logic required to operate within the context of that backend. The classes of the concrete implementation extend the contract defined by the base repository classes. In the example of Firestore, the FirestoreBaseRepository class provides a Firestore-specific implementation of the base repository contract which encapsulates the Firestore-specific logic required to interact with Firestore.
A Postgres or SQL implementation could be built by extending the base repository contract and implementing the required methods.
The concrete implementation layer is built around Firestore as the data store:
FirestoreBaseRepository
An abstract class that:
- Implements the BaseRepository interface
- Provides Firestore-specific functionality
- Manages collection references and groups
- Handles filtering and transactions
- Serves as the foundation for all Firestore-specific repositories
Concrete Repositories
Each entity type has its own concrete repository class that:
- Extends FirestoreBaseRepository
- Implements its corresponding specialized interface
- Provides entity-specific implementation details
- Manages Firestore collections for that entity
Supporting Components
- FirestoreFilterAdapter: Transforms generic filters into Firestore-compatible queries
- FirestoreImplementation: Acts as a factory, providing access to repository and service instances
Benefits of This Architecture
- Abstraction: The interface-based approach abstracts data access details from business logic
- Flexibility: Easily switch between different data stores by implementing new concrete repositories
- Type Safety: Generic typing ensures compile-time type checking
- Maintainability: Consistent patterns make the codebase easier to maintain
- Testability: Interface-based design enables easy mocking for unit tests
- Scalability: New entity types can be added by following the established pattern