Common Patterns & Use Cases

Explore how ONDEMANDENV's core concepts are applied to solve common challenges in managing distributed systems. Note that these patterns inherently involve secure cross-account interactions, orchestrated by the platform based on contracts defined in `contractsLib`.

Pattern: Isolated Full SDLC Environments per Service Branch

Problem: Traditional development often relies on a few shared, static environments (e.g., Dev, QA, Staging) which leads to contention, configuration drift, and deployment bottlenecks. Even modern containerized approaches using ephemeral namespaces often only provide isolated runtime environments, lacking the full context of infrastructure dependencies, versioned configurations, and cross-service interactions inherent in the complete SDLC. This forces rapid merging of small changes, hindering the development of complex features in true isolation.

ONDEMANDENV Solution: ONDEMANDENV promotes a shift towards isolated, full SDLC environments for each meaningful branch of a service. By cloning a stable base Enver (representing a complete Bounded Context), developers get a high-fidelity replica encompassing not just the service's code, but its specific infrastructure definition, versioned configuration, and resolved dependencies – essentially, an entire, independent lifecycle instance. This contrasts sharply with shared static environments or simple container/namespace isolation. It enables deep, meaningful branching strategies where complex features can be developed, deployed, and tested holistically over time within their own dedicated, consistent, and fully isolated SDLC environment, preventing conflicts and providing rapid, reliable feedback loops.

Conceptual Workflow:

  1. Developer Creates Feature Branch: A developer starts work on a new feature or bug fix by creating a new Git branch (e.g., `feature/new-auth-flow`) from a base branch (e.g., `dev`).
  2. Code & Commit with Clone Command: The developer makes code changes in their branch. When ready to test within its own lifecycle, they commit the changes including the special command odmd: create@dev (or the relevant base Enver branch name) in the commit message body.
  3. Platform Provisions Full SDLC Clone: ONDEMANDENV detects the command upon push. It automatically:
    • Creates a new dynamic Enver associated with the `feature/new-auth-flow` branch.
    • Resolves dependencies based on the state of the `dev` Enver (as specified in the command).
    • Deploys a full-stack SDLC clone (infrastructure, configuration, application) using the code from the `feature/new-auth-flow` branch into the designated target account/region, ensuring resource names are unique to this clone.
  4. Isolated SDLC Testing & Iteration: The developer receives the endpoint or access details for their dedicated clone Enver. They can perform integration tests, manual testing, or further iteration within their dedicated SDLC environment without impacting any other developer or shared environment.
  5. Merge & Delete Clone Environment: Once testing is complete and the feature is merged, the developer can trigger the clone environment's destruction by pushing an empty commit with odmd: delete in the message body on the feature branch. The platform cleans up all resources associated with the clone.

This pattern fundamentally changes the development dynamic, moving beyond shared bottlenecks and superficial ephemeral testing. It enables true parallel development of complex features within complete, isolated SDLC environments managed as code, allowing teams to innovate faster and more reliably.

Pattern: Managing Shared Networking Resources (VPC/TGW)

Problem: A dedicated networking team manages core infrastructure (VPCs, TGW, IPAM) in a central account. Application teams in separate workspace accounts need to consume these resources consistently and securely without managing the networking infrastructure themselves.

ONDEMANDENV Solution: Define networking infrastructure as a dedicated "Platform Enver" (e.g., `networkingProd`) in the networking account. This Enver publishes network details (VPC IDs, Subnet IDs, TGW ID) as `Products` in `contractsLib`. Application Envers in workspace accounts declare `Consumers` for these Products. The platform securely resolves these cross-account dependencies using the contracts and handles necessary resource sharing (e.g., RAM) and IAM permissions, providing the application Enver's CDK code with the required values.

1. Define Networking Platform Enver (`contractsLib`)

The networking team defines their Enver and explicitly declares the Outputs Product.

// In MyOrgContracts.ts (contractsLib)
import { OdmdBuild, OdmdEnverCdk, Product } from '@ondemandenv/odmd-contracts-base';
// Assuming NetworkingPlatformEnver extends OdmdEnverCdk

// Define the build configuration for the Networking Enver type
const networkingBuild = new OdmdBuild(this, 'NetworkingBuild', {
    githubRepoAlias: 'networking-infra-repo',
    buildType: 'cdk',
    /* ... potentially sourcePath ... */
});

// Define the specific "prod" instance (Enver)
const networkingProd = new NetworkingPlatformEnver(this, 'NetworkingProd', {
    build: networkingBuild, // Link to build config
    targetAccountAlias: 'networking-account',
    immutable: true, // Assuming prod is immutable
    // Declare the single 'Outputs' product this Enver will publish
    outputsProduct: new Product(this, 'Outputs'),
});

2. Implement Networking CDK Stack (in `networking-infra-repo`)

The networking team's CDK code provisions resources and uses the single `OdmdShareOut` construct to publish a JSON string containing all outputs.

// Simplified example in networking-infra-repo/lib/networking-stack.ts
import * as cdk from 'aws-cdk-lib';
import { OdmdEnverCdk, OdmdShareOut } from '@ondemandenv/odmd-contracts-base';
// ... provision VPC, TGW, Subnets ...

// Structure outputs
const outputs = {
    sharedVpcId: vpc.vpcId,
    privateSubnetIds: vpc.privateSubnets.map(s => s.subnetId),
    // ... other outputs ...
};

// Share outputs via the single 'Outputs' Product ID
new OdmdShareOut(this, 'Outputs', { value: cdk.Stack.of(this).toJsonString(outputs) });

// Platform might also configure AWS RAM sharing here based on consumers in contractsLib

3. Consume Networking Products in Application Enver (`contractsLib`)

An application team declares a `Consumer` for the networking Enver's `Outputs` Product.

// In MyOrgContracts.ts (contractsLib)
import { OdmdBuild, Consumer, Product } from '@ondemandenv/odmd-contracts-base';
// Assuming OrderServiceEnver extends OdmdEnverCdk

// Define the build for the Order Service
const orderServiceBuild = new OdmdBuild(this, 'OrderServiceBuild', { /* ... */ });

// Define the dev instance of the Order Service
const orderServiceDev = new OrderServiceEnver(this, 'OrderServiceDev', {
    build: orderServiceBuild,
    targetAccountAlias: 'dev-workspace-account',
    outputsProduct: new Product(this, 'Outputs'), // Its own outputs
    // Consume the single 'Outputs' product published by the networkingProd Enver
    // Consumers retrieve the JSON string and parse it in their CDK code
    networkingOutputsConsumer: new Consumer(this, 'NetworkingOutputs', networkingProd.outputsProduct),
});

The platform ensures the `OrderServiceDev` CDK stack receives the correct JSON string containing VPC/Subnet IDs from the networking account, allowing it to parse and deploy resources correctly.

Pattern: Deploying Applications to a Shared EKS Cluster

Problem: A platform team manages shared EKS clusters in a dedicated account (`workspace0` or `eks-account`). Application teams need to deploy workloads securely onto these clusters from their own workspace accounts, requiring cluster details, cross-account IAM permissions (IRSA), and manifest deployment capabilities.

ONDEMANDENV Solution: Define the shared EKS cluster as a Platform Enver (`sharedEksProd`) publishing cluster details (OIDC ARN, Kubectl Role ARN) as `Products`. Application Envers (`myAppEksDev`) in workspace accounts consume these `Products`. The application's CDK stack uses the consumed OIDC ARN to define pod-specific IAM Roles (IRSA) in the application's account. The platform uses the consumed Kubectl Role ARN to assume permissions into the EKS cluster account and deploy the cdk8s-generated manifests defined within the application's CDK stack. This keeps infrastructure (IAM) and runtime (k8s) definitions together, managed atomically within the application Enver, while facilitating secure cross-account deployment.

1. Define EKS Platform Enver (`contractsLib`)

The platform team defines the EKS Enver, publishing necessary details within its single `Outputs` Product.

// In MyOrgContracts.ts (contractsLib)
import { OdmdBuild, OdmdEnverCdk, Product } from '@ondemandenv/odmd-contracts-base';
// Assuming EksPlatformEnver extends OdmdEnverCdk

const eksBuild = new OdmdBuild(this, 'EksBuild', { /* ... */ });

const sharedEksProd = new EksPlatformEnver(this, 'SharedEksProd', {
    build: eksBuild,
    targetAccountAlias: 'platform-workspace-account',
    immutable: true, // Assuming prod EKS is immutable
    // Products enabling cross-account deployment & IRSA are nested within Outputs
    outputsProduct: new Product(this, 'Outputs'), // Contains ClusterName, OidcArn, KubectlRoleArn etc.
});

2. Define Application Enver Consuming EKS Details (`contractsLib`)

The application team defines their Enver, consuming the `Outputs` Product from the EKS Enver.

// In MyOrgContracts.ts (contractsLib)
import { OdmdBuild, Consumer, Product } from '@ondemandenv/odmd-contracts-base';
// Assuming MyAppEksEnver extends OdmdEnverCdk
// Assuming myAppImgEnver is defined elsewhere and has an outputsProduct containing imageUri

const myAppBuild = new OdmdBuild(this, 'MyAppBuild', { /* ... CDK/cdk8s build ... */ });

const myAppEksDev = new MyAppEksEnver(this, 'MyAppEksDev', {
    build: myAppBuild,
    targetAccountAlias: 'app-dev-workspace-account',
    outputsProduct: new Product(this, 'Outputs'), // Its own outputs
    // Consume the single 'Outputs' product from the EKS Enver
    eksOutputsConsumer: new Consumer(this, 'EksOutputs', sharedEksProd.outputsProduct),
    // Consume image URI (potentially from another Enver's Outputs product)
    appImageOutputsConsumer: new Consumer(this, 'AppImageOutputs', myAppImgEnver.outputsProduct),
});

3. Implement Application CDK Stack (IAM + k8s Manifests)

The application's CDK stack consumes the EKS outputs JSON, parses it, defines the app-specific IAM role, and the Kubernetes manifests.

// Simplified example in my-app-repo/lib/app-eks-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as cdk8s from 'cdk8s';
import { OdmdEnverCdk } from '@ondemandenv/odmd-contracts-base';

// Interfaces for parsed outputs
interface EksOutputs { clusterOidcArn: string; /* ... other fields ... */ }
interface AppImageOutputs { imageUri: string; /* ... */ }

export class MyAppEksStack extends OdmdEnverCdk {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    // --- Consume and Parse shared values ---
    const eksOutputsJson = OdmdEnverCdk.getSharedValue('EksOutputs');
    const appImageOutputsJson = OdmdEnverCdk.getSharedValue('AppImageOutputs');
    const eksOutputs: EksOutputs = JSON.parse(eksOutputsJson || '{}');
    const appImageOutputs: AppImageOutputs = JSON.parse(appImageOutputsJson || '{}');

    // --- Define App-Specific IAM Role (IRSA) in App Account ---
    const podRole = new iam.Role(this, 'PodRole', {
        // Trust policy uses OIDC ARN parsed from consumed EKS Outputs
        assumedBy: new iam.FederatedPrincipal(eksOutputs.clusterOidcArn, { /* ... conditions ... */ }, 'sts:AssumeRoleWithWebIdentity'),
    });
    // Grant permissions to podRole as needed

    // --- Define Kubernetes Manifests using cdk8s ---
    const app = new cdk8s.App();
    const chart = new cdk8s.Chart(app, 'MyAppChart');
    // ... Define Deployment (using appImageOutputs.imageUri), ServiceAccount (annotated with podRole.roleArn) ...

    // Platform deployment logic implicitly uses the KubectlRoleArn (parsed from EksOutputs)
    // to assume role in the EKS cluster account and apply these manifests.
    // Note: OdmdShareOut for this stack's outputs is omitted for brevity
  }
}

ONDEMANDENV orchestrates the cross-account deployment securely, ensuring the application team manages their IAM and K8s configurations together within their Enver.

Pattern: Enabling Advanced Deployments (Blue/Green, A/B)

Problem: Implementing Blue/Green or A/B testing often involves duplicating configurations, managing complex traffic shifting logic, and ensuring consistency across potentially different accounts or infrastructure variants.

ONDEMANDENV Solution: Leverage distinct Envers for each application version and a dedicated Routing/Traffic Management Enver to control user traffic flow.

  • Application Versions as Envers:
    • Blue/Green: Maintain two distinct, stable Envers (e.g., `myApp-blue` and `myApp-green`), potentially targeting different accounts or the same one. Each publishes its unique endpoint URL or service identifier as a `Product`.
    • A/B Testing / Experimentation: Create `Clones` of a base Enver or define distinct feature Envers. Each variant publishes its endpoint `Product`.
  • Dedicated Routing Enver: Create a separate Enver specifically to manage traffic distribution. This Enver would:
    • `Consume` the endpoint `Products` published by the relevant application Envers (e.g., blue endpoint, green endpoint, variant A endpoint, variant B endpoint).
    • Control the traffic shifting mechanism, such as:
      • Updating weighted DNS records (e.g., Route 53 CNAME weights).
      • Configuring API Gateway stages or routes.
      • Modifying Load Balancer target group weights or listener rules.
    • Deployments to this Routing Enver directly control the user traffic percentage split or cutover between application versions.
This separation ensures the application deployment lifecycle (managed by app Envers) is decoupled from the traffic routing lifecycle (managed by the Routing Enver). Because Envers manage their respective stacks consistently, even across accounts, creating and managing these multiple versions and controlling traffic flow becomes significantly more reliable and manageable. The platform handles the underlying cross-account complexity for both application deployments and routing configurations based on the definitions in `contractsLib`.

Pattern: AI-Assisted Development within Bounded Contexts

Problem: AI code generation tools, while powerful, often produce code that violates architectural boundaries, introduces tight coupling, or creates accidental complexity. Without proper constraints and context, AI can generate technically correct but architecturally problematic solutions that compromise long-term maintainability and system coherence. Traditional development approaches lack the structured contracts and boundaries needed to guide AI toward producing well-architected, Domain-Driven Design-compliant code.

ONDEMANDENV Solution: ONDEMANDENV provides the perfect framework for AI-assisted development by establishing clear bounded contexts, explicit contracts, and isolated testing environments. The platform's `contractsLib` serves as a specification that AI can reference to generate code that respects domain boundaries, uses correct interfaces, and integrates properly with the existing system. This approach transforms AI from a potential source of accidental complexity into a powerful tool for generating maintainable, DDD-compliant code.

AI-Guided Development Workflow:

  1. Define Bounded Context in contractsLib: Establish clear boundaries, responsibilities, and contracts for your domain before involving AI in code generation.
  2. Provide AI with Architectural Context: Share the relevant `BuildDefinition`, `Enver` definitions, and `Product`/`Consumer` contracts with your AI assistant to establish architectural constraints.
  3. Generate Code within Boundaries: Request AI to generate code that operates within the defined bounded context, referencing specific contracts and dependency interfaces.
  4. Validate in Isolated Environment: Use ONDEMANDENV's cloning capability to create an ephemeral environment for testing AI-generated code without affecting shared infrastructure.
  5. Iterate with Contract Evolution: If AI suggests interface changes, evolve contracts through the proper governance process in `contractsLib` before implementing.

Example: AI-Assisted Microservice Development

Consider developing a new authentication service using AI assistance within ONDEMANDENV:

1. Define the Bounded Context (`contractsLib`)

// In MyOrgContracts.ts
const authServiceBuild = new OdmdBuild(this, 'AuthServiceBuild', {
    githubRepoAlias: 'auth-service-repo',
    buildType: 'cdk',
});

const authServiceDev = new AuthServiceEnver(this, 'AuthServiceDev', {
    build: authServiceBuild,
    targetAccountAlias: 'dev-workspace-account',
    // Define what this service will provide
    outputsProduct: new Product(this, 'Outputs'), // JWT endpoints, user validation APIs
    // Define dependencies it needs
    userDbConsumer: new Consumer(this, 'UserDbOutputs', userDbEnver.outputsProduct),
    auditLogConsumer: new Consumer(this, 'AuditLogOutputs', auditLogEnver.outputsProduct),
});

2. AI Code Generation with Architectural Context

Provide your AI assistant with the contract definition and request code generation:

// AI Prompt: "Generate a CDK stack for the AuthServiceEnver that:
// 1. Consumes UserDb outputs for user data access
// 2. Consumes AuditLog outputs for security logging  
// 3. Publishes JWT endpoint and validation API as outputs
// 4. Follows Application-Centric Infrastructure principles
// 5. Uses the consumed database connection for user authentication"

export class AuthServiceStack extends cdk.Stack {
    constructor(scope: Construct, id: string, props: cdk.StackProps) {
        super(scope, id, props);
        
        // AI-generated code respects contract boundaries
        const userDbConfig = JSON.parse(OdmdEnverCdk.getSharedValue('UserDbOutputs') || '{}');
        const auditConfig = JSON.parse(OdmdEnverCdk.getSharedValue('AuditLogOutputs') || '{}');
        
        // AI generates infrastructure within bounded context
        const authLambda = new lambda.Function(this, 'AuthFunction', {
            // ... AI-generated Lambda configuration
            environment: {
                USER_DB_ENDPOINT: userDbConfig.endpoint,
                AUDIT_LOG_TOPIC: auditConfig.topicArn,
            }
        });
        
        // AI publishes outputs according to contract
        new OdmdShareOut(this, 'Outputs', {
            value: cdk.Stack.of(this).toJsonString({
                jwtEndpoint: authApi.url,
                validationEndpoint: `${authApi.url}/validate`,
            })
        });
    }
}

3. Isolated Testing with Cloning

Test the AI-generated code in isolation:

git commit -m "feat: AI-generated auth service implementation

odmd: create@AuthServiceDev"

This creates a temporary environment where the AI-generated authentication service can be tested without affecting other services or shared infrastructure.

4. Contract Evolution and Governance

If AI suggests adding new outputs or changing interfaces, update `contractsLib` through proper governance:

// AI suggests adding password reset capability
// Update contract in contractsLib through PR process
const authServiceDev = new AuthServiceEnver(this, 'AuthServiceDev', {
    // ... existing configuration
    outputsProduct: new Product(this, 'Outputs'), // Now includes password reset endpoints
    // AI identifies need for email service
    emailServiceConsumer: new Consumer(this, 'EmailServiceOutputs', emailServiceEnver.outputsProduct),
});

Benefits of AI + ONDEMANDENV + DDD

This pattern demonstrates how ONDEMANDENV transforms AI from a potential source of technical debt into a powerful ally for building maintainable, well-architected systems that follow Domain-Driven Design principles.