Skip to content

Refactor service struct to group repositories by domain #538

@NathaelB

Description

@NathaelB

Problem

The Service struct currently has 18 generic parameters, making it extremely verbose and hard to maintain:

pub struct Service<R, C, U, CR, H, AS, RU, RO, KS, UR, URA, HC, W, RT, RC, SE, LC, LR>
where
    R: RealmRepository,
    C: ClientRepository,
    U: UserRepository,
    CR: CredentialRepository,
    H: HasherRepository,
    AS: AuthSessionRepository,
    RU: RedirectUriRepository,
    RO: RoleRepository,
    KS: KeyStoreRepository,
    UR: UserRoleRepository,
    URA: UserRequiredActionRepository,
    HC: HealthCheckRepository,
    W: WebhookRepository,
    RT: RefreshTokenRepository,
    RC: RecoveryCodeRepository,
    SE: SecurityEventRepository,
    LC: LdapConnector,
    LR: LdapRepository,

This leads to:

  • Extremely long generic constraints on every service implementation
  • Poor readability and maintainability
  • Difficulty adding new repositories
  • Complex type signatures throughout the codebase

Solution

Group repositories by domain to reduce generic complexity while maintaining logical organization:

Create domain-specific repository containers

  • Core repositories (authentication, users, client, etc.):
    • RealmRepository, ClientRepository
  • Abyss domain (LDAP integration)
  • Trident domain (MFA/Recovery code)
  • Seawatch domain (Security events)

Proposed structure

// New domain repository containers
pub struct CoreRepositories<R, C, U, CR, H, AS, RU, RO, KS, UR, URA, HC, W, RT> { ... }
pub struct AbyssRepositories<LC, LR> { ... }
pub struct TridentRepositories<RC> { ... }
pub struct SeawatchRepositories<SE> { ... 

pub struct Service<Core, Abyss, Trident, Seawatch>
where
    Core: CoreRepositories,
    Abyss: AbyssRepositories, 
    Trident: TridentRepositories,
    Seawatch: SeawatchRepositories,
{
    pub(crate) core: Core,
    pub(crate) abyss: Abyss,
    pub(crate) trident: Trident,
    pub(crate) seawatch: Seawatch,
    pub(crate) policy: FerriskeyPolicy<...>,
}

impl<Core, Abyss, Trident, Seawatch> Service<Core, Abyss, Trident, Seawatch>
where
    Core: CoreRepositories,
    Abyss: AbyssRepositories,
    Trident: TridentRepositories,
    Seawatch: SeawatchRepositories,
{
    pub fn realm_repository(&self) -> &Arc<Core::RealmRepository> {
        &self.core.realm_repository
    }
    
    pub fn client_repository(&self) -> &Arc<Core::ClientRepository> {
        &self.core.client_repository
    }
    
    pub fn user_repository(&self) -> &Arc<Core::UserRepository> {
        &self.core.user_repository
    }
    
    // ... other getters
    
    pub fn ldap_connector(&self) -> &Arc<Abyss::LdapConnector> {
        &self.abyss.ldap_connector
    }
    
    pub fn ldap_repository(&self) -> &Arc<Abyss::LdapRepository> {
        &self.abyss.ldap_repository
    }
    
    pub fn recovery_code_repository(&self) -> &Arc<Trident::RecoveryCodeRepository> {
        &self.trident.recovery_code_repository
    }
    
    pub fn security_event_repository(&self) -> &Arc<Seawatch::SecurityEventRepository> {
        &self.seawatch.security_event_repository
    }
}

Benefits

  1. Reduced complexity: From 18 to 4 generic parameters
  2. Logical organization: Repositories grouped by business domain
  3. Backward compatibility: Maintain existing API through getters
  4. Easier maintenance: Adding new repositories is scoped to specific domains
  5. Better readability: Service implementations become much cleaner

Breaking changes

This should be a non-breaking change if implemented correctly with proper getter methods.

Priority

Medium - This is a significant architectural improvement that will make the codebase more maintainable long-term.

Metadata

Metadata

Assignees

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions