Table of Contents

Getting Started

This guide walks you through building your first Arc.Core application from scratch. You'll learn how to set up the application, define commands and queries, and run your service.

Prerequisites

  • .NET 9 SDK or later
  • Basic understanding of C# and .NET concepts

Installation

Add the Arc.Core package to your project:

dotnet add package Cratis.Applications

Basic Setup

Create a new console application and configure Arc.Core:

using Cratis.Arc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

var builder = ArcApplicationBuilder.CreateBuilder(args);

// Add Arc services
builder.AddCratisArc();

// Configure logging
builder.Services.AddLogging(logging =>
{
    logging.AddConsole();
    logging.SetMinimumLevel(LogLevel.Information);
});

// Build and run the application
var app = builder.Build();

// Start the HTTP listener on specified prefixes
app.UseCratisArc("http://localhost:5000/");

Console.WriteLine("Application started on http://localhost:5000/");
Console.WriteLine("Press Ctrl+C to stop...");

await app.RunAsync();

ArcApplicationBuilder API

The ArcApplicationBuilder provides a familiar builder pattern for configuring your application:

Creating a Builder

// Create with command-line arguments
var builder = ArcApplicationBuilder.CreateBuilder(args);

// Or without arguments
var builder = ArcApplicationBuilder.CreateBuilder();

Available Properties

The builder exposes several properties for configuration:

// Configuration system
IConfigurationManager Configuration = builder.Configuration;

// Host environment information
IHostEnvironment Environment = builder.Environment;

// Logging configuration
ILoggingBuilder Logging = builder.Logging;

// Service collection for dependency injection
IServiceCollection Services = builder.Services;

// Metrics configuration
IMetricsBuilder Metrics = builder.Metrics;

Adding Arc Services

builder.AddCratisArc(arcBuilder =>
{
    // Configure Arc-specific options
    // Add extensions like Chronicle, MongoDB, etc.
});

ArcApplication API

Starting the Application

The UseCratisArc method starts the built-in HTTP listener:

// Single prefix
app.UseCratisArc("http://localhost:5000/");

// Multiple prefixes
app.UseCratisArc(
    "http://localhost:5000/",
    "http://+:5001/"
);

// Default (http://localhost:5000/index.md)
app.UseCratisArc();

Running the Application

// Run and block until shutdown (Ctrl+C)
await app.RunAsync();

// Or control start/stop manually
await app.StartAsync();
// ... do work ...
await app.StopAsync();

Working with Commands

Commands represent actions or operations in your application. They're automatically exposed as HTTP POST endpoints.

Defining a Command

using Cratis.Arc.Commands;

public record CreateUser(string Name, string Email) : ICommand;

public class CreateUserHandler(ILogger<CreateUserHandler> logger) : ICommandHandler<CreateUser>
{
    public Task<CommandResult> Handle(CreateUser command, CommandContext context)
    {
        logger.LogInformation("Creating user: {Name}", command.Name);
        
        // Your business logic here
        
        return Task.FromResult(CommandResult.Success);
    }
}

Accessing Command Endpoints

Commands are exposed as POST endpoints:

curl -X POST http://localhost:5000/api/your-app/create-user \
  -H "Content-Type: application/json" \
  -d '{"name":"John Doe","email":"john@example.com"}'

Working with Queries

Queries represent data retrieval operations. They're automatically exposed as HTTP GET endpoints.

Defining a Query

using Cratis.Arc.Queries;

public record GetUser(Guid Id) : IQuery<UserDto>;

public class GetUserHandler : IQueryHandler<GetUser, UserDto>
{
    public Task<UserDto> Handle(GetUser query, QueryContext context)
    {
        // Your query logic here
        return Task.FromResult(new UserDto(query.Id, "John Doe"));
    }
}

public record UserDto(Guid Id, string Name);

Accessing Query Endpoints

Queries are exposed as GET endpoints:

curl http://localhost:5000/api/your-app/get-user?id=123e4567-e89b-12d3-a456-426614174000

Configuration

Using appsettings.json

Arc.Core supports standard .NET configuration:

{
  "Cratis": {
    "Arc": {
      "GeneratedApis": {
        "RoutePrefix": "api"
      },
      "CorrelationId": {
        "HttpHeader": "X-Correlation-ID"
      },
      "Tenancy": {
        "HttpHeader": "X-Tenant-ID"
      }
    }
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning"
    }
  }
}

Environment-Specific Configuration

Use environment-specific files following .NET conventions:

  • appsettings.json - Base configuration
  • appsettings.Development.json - Development overrides
  • appsettings.Production.json - Production overrides
var builder = ArcApplicationBuilder.CreateBuilder(args);

// Configuration is automatically loaded based on environment
var environment = builder.Environment.EnvironmentName;
Console.WriteLine($"Running in {environment} environment");

Custom Configuration Section

Specify a custom configuration section path:

builder.AddCratisArc(
    configSectionPath: "MyApp:ArcSettings"
);

Adding Services

Logging

builder.Services.AddLogging(logging =>
{
    logging.AddConsole();
    logging.AddDebug();
    logging.SetMinimumLevel(LogLevel.Information);
});

Custom Services

// Singleton
builder.Services.AddSingleton<IMyService, MyService>();

// Scoped (per request)
builder.Services.AddScoped<IUserRepository, UserRepository>();

// Transient (per injection)
builder.Services.AddTransient<IEmailSender, EmailSender>();

Using Arc Conventions

Arc supports automatic service registration using attributes:

// Services with [Singleton], [Scoped], or [Transient] attributes
// are automatically registered
[Singleton]
public class MyService : IMyService
{
    // Implementation
}

Complete Example

Here's a complete working example that demonstrates commands, queries, and services:

using Cratis.Arc;
using Cratis.Arc.Commands;
using Cratis.Arc.Queries;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

var builder = ArcApplicationBuilder.CreateBuilder(args);

builder.AddCratisArc();

builder.Services.AddLogging(logging =>
{
    logging.AddConsole();
    logging.SetMinimumLevel(LogLevel.Information);
});

var app = builder.Build();

app.UseCratisArc("http://localhost:5000/");

Console.WriteLine("Application started!");
Console.WriteLine("Available endpoints:");
Console.WriteLine("  POST   http://localhost:5000/api/my-app/greet");
Console.WriteLine("  GET    http://localhost:5000/api/my-app/get-greeting?name=World");
Console.WriteLine("  GET    http://localhost:5000/.cratis/me");
Console.WriteLine();
Console.WriteLine("Press Ctrl+C to stop...");

await app.RunAsync();

// Commands
public record Greet(string Name) : ICommand;

public class GreetHandler(ILogger<GreetHandler> logger) : ICommandHandler<Greet>
{
    public Task<CommandResult> Handle(Greet command, CommandContext context)
    {
        logger.LogInformation("Greeting {Name}", command.Name);
        return Task.FromResult(CommandResult.Success);
    }
}

// Queries
public record GetGreeting(string Name) : IQuery<string>;

public class GetGreetingHandler : IQueryHandler<GetGreeting, string>
{
    public Task<string> Handle(GetGreeting query, QueryContext context)
    {
        return Task.FromResult($"Hello, {query.Name}!");
    }
}

Advanced Scenarios

Background Services

Combine Arc.Core with IHostedService for background processing:

public class BackgroundWorker(ILogger<BackgroundWorker> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation("Background worker started");
        
        while (!stoppingToken.IsCancellationRequested)
        {
            // Do background work
            await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
        }
        
        logger.LogInformation("Background worker stopped");
    }
}

// Register it
builder.Services.AddHostedService<BackgroundWorker>();

Integrating with Chronicle

Add event sourcing capabilities:

builder.AddCratisArc(arcBuilder =>
{
    arcBuilder.WithChronicle();
});

Integrating with MongoDB

Add MongoDB support:

builder.AddCratisArc(arcBuilder =>
{
    arcBuilder.WithMongoDB();
});

Troubleshooting

Endpoints Not Found

Ensure you've called app.UseCratisArc() before app.RunAsync():

var app = builder.Build();
app.UseCratisArc("http://localhost:5000/");  // Must be called!
await app.RunAsync();

HTTP Listener Errors

If you get HTTP listener errors, ensure:

  1. The port is not already in use
  2. You have permissions to bind to the port (on Windows, non-admin users can't bind to port 80)
  3. Use http://+:5001/ instead of http://localhost:5001/ to listen on all interfaces

Configuration Not Loading

Ensure appsettings.json is copied to output:

<ItemGroup>
  <None Update="appsettings*.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

Next Steps

Now that you have a basic Arc.Core application running, explore these topics:

  • Authentication - Implement custom authentication handlers
  • Authorization - Protect your endpoints with authorization attributes
  • Commands - Learn about advanced command patterns
  • Queries - Discover query features like filtering and pagination
  • Identity - Integrate the identity system
  • Tenancy - Configure multi-tenant applications
  • Validation - Add validation to commands and queries