Skip to content

Generator: Create Facade Pattern #37

@JerrettDavis

Description

@JerrettDavis

Summary

Add a source generator that produces boilerplate-free, GoF-consistent Facade pattern implementations for subsystems, enabling an explicit “front door” API over multiple collaborators.

The generator lives in PatternKit.Generators and emits self-contained, readable C# with no runtime PatternKit dependency.

Primary goals:

  • Define a façade surface that is explicitly mapped to subsystem operations.
  • Keep mappings deterministic and easy to debug.
  • Support both interface- and class-based façade shapes.
  • Provide optional grouping/organization and diagnostics for drift.

Motivation / Problem

Facade implementations are often:

  • hand-written thin wrappers that drift as subsystems evolve
  • inconsistent about naming and responsibility boundaries
  • missing documentation for “what is actually exposed”

A generator can:

  • formalize a façade contract
  • ensure exposed operations stay in sync
  • provide guardrails and tests

Supported Targets (must-have)

The generator must support:

  • partial class / partial struct
  • partial record class / partial record struct

Facade can be generated as:

  1. Facade type: annotate a partial façade contract and map methods.
  2. Facade host: annotate a static/instance host containing mapping methods.

Proposed User Experience

A) Contract-first facade (interface)

public interface IBillingSubsystem
{
    Receipt Pay(PaymentRequest req);
    Refund Refund(RefundRequest req);
}

[GenerateFacade]
public partial interface IBillingFacade
{
    Receipt Pay(PaymentRequest req);
    Refund Refund(RefundRequest req);
}

public sealed class BillingFacadeImpl
{
    private readonly IBillingSubsystem _billing;

    public BillingFacadeImpl(IBillingSubsystem billing) => _billing = billing;

    [FacadeMap]
    private Receipt PayImpl(PaymentRequest req) => _billing.Pay(req);

    [FacadeMap]
    private Refund RefundImpl(RefundRequest req) => _billing.Refund(req);
}

Generated (representative shape):

  • A façade implementation that wires each contract method to the mapped method.

B) Host-first facade (mapping methods define surface)

[GenerateFacade(FacadeTypeName = "BillingFacade")]
public static partial class BillingFacadeHost
{
    [FacadeExpose]
    public static Receipt Pay(IBillingSubsystem billing, PaymentRequest req) => billing.Pay(req);

    [FacadeExpose]
    public static Refund Refund(IBillingSubsystem billing, RefundRequest req) => billing.Refund(req);
}

Generated:

  • BillingFacade type with instance methods, constructed with IBillingSubsystem.

Attributes / Surface Area

Namespace: PatternKit.Generators.Facade

Core

  • [GenerateFacade] on facade contract or host

    • string? FacadeTypeName (default: <Name>Facade)
    • bool GenerateAsync (default: inferred)
    • bool ForceAsync (default: false)
    • FacadeMissingMapPolicy MissingMap (default: Error)

Mapping attributes:

  • [FacadeMap] identifies the implementation method backing a contract method.
  • [FacadeExpose] identifies host methods to expose.
  • [FacadeIgnore] excludes contract members.

Policies:

  • MissingMapPolicy: Error | Stub (v2) | Ignore (discouraged)

Semantics (must-have)

Deterministic mapping

  • Contract-first:

    • each facade contract member must map to exactly one [FacadeMap] method.
    • mapping by explicit attribute, not by name convention (v1).
  • Host-first:

    • each [FacadeExpose] method becomes a facade method.

Async

  • If any mapping method returns ValueTask<T>/Task<T> or accepts CancellationToken, emit async facade variants.
  • Prefer ValueTask for generated async facade methods.

Naming and organization

  • Keep generated code readable:

    • clear regions or comments per method
    • deterministic ordering by contract member name

Diagnostics (must-have)

Stable IDs, actionable:

  • PKFCD001 Type marked [GenerateFacade] must be partial.
  • PKFCD002 No mapped methods found for facade members.
  • PKFCD003 Multiple mappings found for a single facade member.
  • PKFCD004 Map method signature mismatch.
  • PKFCD005 Facade type name conflicts with existing type.
  • PKFCD006 Async mapping detected but async generation disabled.

Generated Code Layout

  • Name.Facade.g.cs

Determinism:

  • stable ordering by member name, then signature.

Testing Expectations

  • Contract-first mapping routes to correct subsystem calls.

  • Host-first generates façade type correctly.

  • Async path respects cancellation.

  • Diagnostics:

    • missing mapping
    • duplicate mapping
    • signature mismatch

Acceptance Criteria

  • [GenerateFacade] works for class/struct/record class/record struct (host) and supports interface contracts.
  • Deterministic mapping and generated method ordering.
  • Async variants generated when needed (ValueTask preferred).
  • Diagnostics cover missing/duplicate/mismatch mappings.
  • Tests cover routing, async behavior, and diagnostics.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions