Documentation

operations/documentation-setup.md

Dynaplex Documentation Setup

This guide explains how to build, preview, and deploy the Dynaplex documentation site built with DocFX.

Overview

The documentation system uses:

  • DocFX - Generates static website from C# XML comments and markdown
  • XML Documentation - Auto-generated API reference from code comments
  • Markdown Files - Conceptual documentation (architecture, guides, ADRs)
  • CI/CD - Automatic builds and deployment (Azure DevOps or GitLab)

Prerequisites

Required

  • .NET 9 SDK - For building the project
  • DocFX - For generating documentation

Installation

# Install .NET 9 SDK (if not already installed)
# Download from: https://dotnet.microsoft.com/download

# Install DocFX
dotnet tool install -g docfx

# Verify installation
docfx --version

Project Structure

acsis-core/
├── docfx.json              # DocFX configuration
├── toc.yml                 # Main table of contents
├── index.md                # Documentation homepage
├── api/                    # Generated API reference (auto-created)
│   └── index.md           # API documentation index
├── docs/                   # Conceptual documentation
│   ├── *.md               # Architecture docs
│   └── adrs/              # Architecture Decision Records
├── engines/               # Engine source code
│   └── */resources/       # Component-specific docs
├── _site/                 # Generated website (gitignored)
└── Directory.Build.props  # Enables XML documentation

Building Documentation Locally

Step 1: Build the Project

XML documentation is generated during build:

# Build all projects (generates XML files)
dotnet build acsis-core.slnx

Step 2: Generate API Metadata

Extract API reference from XML comments:

# Generate API metadata
docfx metadata docfx.json

This creates YAML files in the api/ folder with API structure.

Step 3: Build Documentation Site

Build the complete documentation website:

# Build documentation
docfx build docfx.json

Output is in _site/ folder.

Step 4: Preview Locally

Serve the documentation locally:

# Serve documentation
docfx serve _site

# Or use the shorthand that builds and serves
docfx docfx.json --serve

Open browser to: http://localhost:8080

Running with Containers

Add the documentation container to your Aspire AppHost:

// In your AppHost.cs (e.g., projects/assettrak-classic/src/Acsis.Dynaplex.Projects.AssetTrakClassic/AppHost.cs)
var foundation = builder.AddFoundation()
    .WithAppConfiguration()
    .WithAppInsights()
    .WithContainerAppEnvironment()
    .WithDefaultDatabase()
    .WithCoreData<Acsis_Components_CoreData, Acsis_Components_CoreData_DbMigrator>()
    .WithSystemEnvironment<Acsis_Components_SystemEnvironment, Acsis_Components_SystemEnvironment_DbMigrator>()
    .WithIdentity<Acsis_Components_Identity, Acsis_Components_Identity_DbMigrator>()
    .WithEvents<Acsis_Components_Events, Acsis_Components_Identity_DbMigrator>()
    // ... other components ...
    .WithDocumentation();  // Add this line

foundation.Build();
builder.Build().Run();

Benefits:

  • Documentation runs alongside all services
  • Accessible via Aspire Dashboard
  • Automatic rebuilds when container configuration changes
  • Only runs in non-production environments (by default)

Access:

  • Via Aspire Dashboard at the docs-http endpoint
  • Direct access at the port shown in Aspire Dashboard

Skip in Production:
By default, .WithDocumentation() skips the container when publishing to Azure. To include it:

.WithDocumentation(skipInProduction: false)

Option 2: Standalone Docker Container

Run the documentation in a standalone Docker container:

# Build and run using Docker directly
docker build -f Dockerfile.docs -t dynaplex-docs .
docker run -p 8080:80 dynaplex-docs

# Or use docker-compose
docker-compose -f docker-compose.docs.yml up

# Run in background
docker-compose -f docker-compose.docs.yml up -d

# Stop the container
docker-compose -f docker-compose.docs.yml down

Access: http://localhost:8080

Container Features:

  • Multi-stage build (builds docs in .NET SDK, serves with nginx)
  • Health checks for container monitoring
  • Gzip compression enabled
  • Lightweight nginx:alpine base (~40MB)

Option 3: Docker with Custom Port

Use a different port to avoid conflicts:

# Run on port 9090
docker run -p 9090:80 dynaplex-docs

# Or edit docker-compose.docs.yml
services:
  docs:
    ports:
      - "9090:80"  # Change to your preferred port

Container Architecture

The Dockerfile.docs uses a multi-stage build:

Stage 1: Build

  • Uses mcr.microsoft.com/dotnet/sdk:9.0
  • Installs DocFX
  • Builds all C# projects
  • Generates API metadata
  • Builds documentation site

Stage 2: Serve

  • Uses nginx:alpine (lightweight)
  • Copies built _site/ from build stage
  • Configures nginx for SPA routing
  • Enables gzip compression
  • Includes health check endpoint

Why This Approach?

  • Single image - No external dependencies
  • Self-contained - All docs built into the image
  • Fast serving - nginx is optimized for static content
  • Small size - Final image is ~40MB
  • Health monitoring - Kubernetes/Aspire can monitor health

Quick Commands

# Build and serve in one command
docfx docfx.json --serve

# Force rebuild
docfx build docfx.json --force

# Clean generated files
rm -rf _site api obj

Writing Documentation

XML Documentation Comments

Add XML comments to your C# code:

/// <summary>
/// Gets all active items from the catalog.
/// </summary>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>List of items</returns>
/// <exception cref="InvalidOperationException">Thrown when database is unavailable</exception>
public async Task<List<ItemResponse>> GetItemsAsync(CancellationToken cancellationToken = default)
{
    // Implementation
}

These comments automatically appear in the API reference.

Markdown Documentation

Create markdown files in appropriate locations:

  • Architecture docsdocs/
  • Component docsengines/{component}/resources/
  • ADRsdocs/adrs/

Reference other pages using relative links:

See [Architectural Patterns](../ARCHITECTURAL_PATTERNS_QUICK_REF.md) for details.

Adding New Sections

Update toc.yml to add new sections:

- name: New Section
  items:
  - name: Page Title
    href: path/to/page.md

CI/CD Deployment

Azure DevOps

The pipeline is configured in azure-pipelines-docs.yml.

Setup Steps:

  1. Create Pipeline:

    • Go to Azure DevOps → Pipelines → New Pipeline
    • Select repository
    • Choose existing YAML file: azure-pipelines-docs.yml
  2. Configure Deployment (choose one):

    Option A: Azure Static Web Apps

    # Uncomment in azure-pipelines-docs.yml
    - task: AzureStaticWebApp@0
      inputs:
        app_location: '_site'
        azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_API_TOKEN)
    

    Option B: Azure Blob Storage

    # Uncomment in azure-pipelines-docs.yml
    - task: AzureCLI@2
      inputs:
        azureSubscription: 'Your-Service-Connection'
        scriptType: 'bash'
        inlineScript: |
          az storage blob upload-batch \
            --account-name yourstorageaccount \
            --source _site \
            --destination '$web' \
            --overwrite
    
  3. Set Variables:

    • AZURE_STATIC_WEB_APPS_API_TOKEN (for Static Web Apps)
    • Or configure service connection (for Blob Storage)

GitLab

The pipeline is configured in .gitlab-ci-docs.yml.

Setup Steps:

  1. Create Pipeline:

    • Rename .gitlab-ci-docs.yml to .gitlab-ci.yml
    • Or include it in your main .gitlab-ci.yml:
      include:
        - local: '.gitlab-ci-docs.yml'
      
  2. GitLab Pages (Automatic):

    • Pipeline automatically deploys to GitLab Pages
    • Access at: https://your-group.gitlab.io/acsis-core
  3. Azure Deployment (Optional):

    • Set CI/CD variables:
      • AZURE_CLIENT_ID
      • AZURE_CLIENT_SECRET
      • AZURE_TENANT_ID
    • Uncomment Azure deployment job in .gitlab-ci-docs.yml

Manual Deployment

Build locally and deploy manually:

# Build documentation
docfx build docfx.json

# Deploy to Azure Static Web Apps
az staticwebapp deploy \
  --name your-app-name \
  --resource-group your-rg \
  --source _site

# Or deploy to Azure Blob Storage
az storage blob upload-batch \
  --account-name yourstorageaccount \
  --source _site \
  --destination '$web' \
  --overwrite

Deployment Options

Pros:

  • Free tier available
  • Custom domains
  • Automatic SSL
  • Built-in authentication
  • Edge caching

Setup:

# Create Static Web App
az staticwebapp create \
  --name dynaplex-docs \
  --resource-group your-rg \
  --location eastus2 \
  --sku Free

Option 2: Azure Blob Storage

Pros:

  • Very cheap (~$0.20/month)
  • Fast CDN integration
  • Simple setup

Setup:

# Create storage account
az storage account create \
  --name dynaplexdocs \
  --resource-group your-rg \
  --sku Standard_LRS

# Enable static website
az storage blob service-properties update \
  --account-name dynaplexdocs \
  --static-website \
  --index-document index.html \
  --404-document 404.html

Option 3: GitLab Pages

Pros:

  • Free
  • Integrated with GitLab
  • Automatic deployment

Access: https://your-group.gitlab.io/acsis-core

Option 4: GitHub Pages

If you move to GitHub:

Setup:

# Create .github/workflows/docs.yml
name: Deploy Docs
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-dotnet@v3
        with:
          dotnet-version: '9.x'
      - run: dotnet tool install -g docfx
      - run: docfx build docfx.json
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./_site

Customization

Branding

Update docfx.json:

{
  "build": {
    "globalMetadata": {
      "_appTitle": "Your Company - Dynaplex",
      "_appFooter": "© 2024 Your Company",
      "_enableSearch": true
    }
  }
}

Templates

Use custom templates:

# Export default template
docfx template export default

# Customize in _exported_templates/default/

# Use custom template
docfx build docfx.json -t default,_exported_templates/default

Custom CSS

Add to docfx.json:

{
  "build": {
    "resource": [
      {
        "files": ["custom.css"]
      }
    ]
  }
}

Troubleshooting

Build Fails with "Missing XML documentation"

Solution: CS1591 warnings are suppressed in Directory.Build.props. If you want to enforce documentation:

<!-- Remove this line from Directory.Build.props -->
<NoWarn>$(NoWarn);CS1591</NoWarn>

API Reference is Empty

Solution: Ensure projects are built first:

dotnet build
docfx metadata docfx.json
docfx build docfx.json

Solution: Use relative paths and check case sensitivity:

<!-- Correct -->
[Link](../docs/GUIDE.md)

<!-- Incorrect -->
[Link](/docs/guide.md)  # Absolute path
[Link](../docs/guide.md)  # Wrong case

Serve Command Not Working

Solution: Ensure port 8080 is not in use:

# Use different port
docfx serve _site -p 9090

Missing Images

Solution: Add images to resource files in docfx.json:

{
  "build": {
    "resource": [
      {
        "files": ["images/**", "docs/images/**"]
      }
    ]
  }
}

Best Practices

  1. Write XML comments as you code - Don't defer documentation
  2. Use meaningful summaries - Explain "why", not just "what"
  3. Include code examples - Use <example> tags
  4. Link related members - Use <see cref=""/> tags
  5. Document exceptions - Use <exception> tags
  6. Keep markdown up to date - Review during code reviews
  7. Test locally before pushing - Run docfx serve to preview
  8. Version documentation - Tag releases and maintain versions

Maintenance

Regular Updates

  • After new features - Update relevant documentation
  • Before releases - Review and update API docs
  • Monthly - Check for broken links and outdated content
  • Quarterly - Review architectural docs and ADRs

Monitoring

Check documentation builds in CI/CD:

  • Azure DevOps: Pipeline status
  • GitLab: CI/CD → Pipelines

Resources

Support

For issues or questions:

  • Create issue in repository
  • Check DocFX GitHub issues
  • Review this documentation

Happy documenting! 📚