Skip to content

Repository structure - provider layout refactor and large file splits  #100

@blindzero

Description

@blindzero

Problem Statement

Several production files remain very large and still mix responsibilities, which makes reviews and maintenance harder and increases merge conflict risk.

E.g.

  • src/IdLE.Provider.AD/Public/New-IdleADIdentityProvider.ps1 (~692 LOC)
  • src/IdLE.Provider.EntraID/Public/New-IdleEntraIDIdentityProvider.ps1 (~873 LOC)
  • src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1 (~782 LOC)
  • src/IdLE.Core/Public/New-IdlePlanObject.ps1 (~671 LOC)

The repo already contains Private/ folders in the affected modules, but they are not yet used for the majority of the logic in the files above. Other additional best-practice oriented folders may be introduced but require confirmation from owner (e.g. Classes)

Non-Goals

  • No functional changes to workflows or step execution behavior.
  • No public contract changes (cmdlet parameters, outputs, exported functions).
  • No new cross-module “core contracts” unless strictly required (avoid scope creep).
  • No new folder taxonomy (e.g. no Internal/ convention introduced here).

Authoritative Decisions

A) Output improvements are allowed

Improving warning/error messages (clarity, actionability) is allowed and does not count as a behavior change in this issue.

Not allowed: structural or functional behavior changes (e.g., changing when an exception is thrown in a way that alters execution paths).

B) Helper granularity

Splitting is guided by cohesive / thematic responsibilities, not LOC thresholds.

C) Provider IO boundary

IO stays in the Adapter.

  • Provider factories remain “facades”.
  • Provider script methods call adapter methods for IO.
  • Private/ helpers are used for mapping, prereq checks, and small logic utilities that are not IO.

D) Prerequisites policy (required vs optional)

This issue introduces a consistent structure for prerequisites checks to avoid accidental timing/throw changes during refactor.

Rules:

  1. Module import MUST remain non-blocking.
  2. Provider creation (New-Idle*IdentityProvider) MUST NOT throw due to missing prerequisites.
    • It SHOULD call Test-Idle<Provider>Prerequisites and emit warnings.
  3. Provider methods that require missing prerequisites MUST throw a clear, actionable error when invoked (fail-fast at call time).
  4. “Required vs optional” is defined per operation:
    • Required: missing → operation cannot succeed → method must throw.
    • Optional: missing → operation may still succeed, but may degrade features/performance; warning is allowed.

Minimal required list (default adapters):

  • AD provider: ActiveDirectory (RSAT) module is required for any operation that uses AD cmdlets via the default adapter.
  • EntraID provider: Graph client dependency used by the default adapter is required for any operation that performs Graph calls.
    • Exact module set depends on current adapter implementation; the prereq helper must reflect the real default adapter requirements.

E) Prerequisites result shape

Test-Idle<Provider>Prerequisites MUST:

  • NOT throw
  • Return a structured PSCustomObject (provider-internal; no new core contract required)

Fields:

  • PSTypeName: IdLE.PrerequisitesResult
  • ProviderName (string)
  • IsHealthy (bool)
  • MissingRequired (string[])
  • MissingOptional (string[])
  • Notes (string[])
  • CheckedAt (datetime)

Proposed Refactor Patterns (normative)

Provider refactor pattern

Keep New-Idle*IdentityProvider responsible only for:

  • Parameter validation (data-only, no IO)
  • Creating/wiring the adapter (dependency injection stays supported)
  • Constructing the provider object and attaching script methods
  • Calling Test-Idle<Provider>Prerequisites and emitting warnings (no throw)

Move implementation details into Private/ helpers:

  • Mapping / conversion
    • ConvertTo-Idle<Provider>* helpers
  • Prerequisites checks
    • Test-Idle<Provider>Prerequisites.ps1 as described above
  • No IO in private helpers for this issue (IO stays in adapter)

Core refactor pattern

  • Keep public function signatures and outputs identical.
  • Replace nested helper functions inside public functions with calls to src/IdLE.Core/Private/* helpers.
  • Prefer extracting cohesive blocks into new Private/ functions (e.g., result construction, provider bag normalization, step invocation wrapper).

Implementation Tasks

1) AD provider: facade + helpers

Target:

  • src/IdLE.Provider.AD/Public/New-IdleADIdentityProvider.ps1

Create new Private/ helpers (names may be adjusted but must follow verb-noun and be consistent):

  • Test-IdleADPrerequisites.ps1
  • ConvertTo-IdleADEntitlement.ps1 (extract entitlement parsing/validation)
  • Additional ConvertTo-* helpers for identity/address parsing if needed

Refactor rules:

  • Public factory remains the only public entrypoint; helpers are not exported.
  • Keep dependency injection: -Adapter continues to work.
  • Ensure IO remains in the adapter.
  • On provider creation, call Test-IdleADPrerequisites and Write-Warning for missing prereqs (no throw).

2) EntraID provider: facade + helpers

Target:

  • src/IdLE.Provider.EntraID/Public/New-IdleEntraIDIdentityProvider.ps1

Create new Private/ helpers:

  • Test-IdleEntraIDPrerequisites.ps1
  • ConvertTo-IdleEntraIDEntitlement.ps1 (and other mapping helpers as needed)

Refactor rules mirror the AD provider rules, including: IO remains in adapter.

3) Core: extract helpers from large public files

Targets:

  • src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1
  • src/IdLE.Core/Public/New-IdlePlanObject.ps1

Tasks:

  • Identify nested helper functions and cohesive internal blocks.
  • Extract them into src/IdLE.Core/Private/ functions (new files as needed).
  • Replace inline implementations with calls to the private helpers.
  • Ensure outputs, events, and error behavior remain functionally identical (message improvements allowed).

4) Tests

  • Keep existing tests green.
  • Add targeted tests only if needed to protect against regressions from extraction.
  • Prefer coverage gains without duplicate assertions.

Quality Gates / Definition of Done

  • Provider factories reduced to facade responsibilities only
  • Mapping and prerequisites moved to Private/ helpers (no IO in helpers)
  • Provider creation does not throw due to missing prerequisites; warnings allowed
  • Provider methods throw actionable errors when required prerequisites are missing
  • Core public functions delegate cohesive logic to Private/ helpers
  • No public contract changes
  • Pester green
  • PSScriptAnalyzer green, no warnings
  • Git status clean
  • examples (demo) running
  • all documentation updated accordingly / where necessary

Metadata

Metadata

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions