Documentation
tutorials/your-first-component.md
Your First Component
Welcome! This tutorial will guide you through understanding and building your first Dynaplex component. By the end, you'll understand component structure and have created a simple working component.
What You'll Learn
- Component structure and organization
- The role of
.Abstractionsprojects - Creating interfaces and models
- Basic implementation patterns
- How components integrate with Dynaplex
Prerequisites
- Local development setup complete
- Getting started guide completed
- Basic understanding of C# and ASP.NET Core
Understanding Component Anatomy
Every Dynaplex component follows a consistent project structure:
engines/my-component/
├── src/
│ ├── Acsis.Dynaplex.Engines.MyComponent.Abstractions/ # 📋 Contracts
│ │ ├── IMyComponentApi.cs # Main interface
│ │ ├── Models/ # DTOs and requests
│ │ └── Configuration/ # Options classes
│ │
│ ├── Acsis.Dynaplex.Engines.MyComponent.Database/ # 🗄️ Database (if needed)
│ │ ├── MyComponentDb.cs # DbContext
│ │ ├── Entities/ # Entity models
│ │ └── Migrations/ # EF Core migrations
│ │
│ ├── Acsis.Dynaplex.Engines.MyComponent/ # ⚙️ Service implementation
│ │ ├── Program.cs # Service entry point
│ │ ├── MyComponentApi.cs # API implementation
│ │ └── Services/ # Business logic
│ │
│ └── Acsis.Dynaplex.Engines.MyComponent.ApiClient/ # 🔗 HTTP client (generated)
│
└── test/ # 🧪 Tests
└── Acsis.Dynaplex.Engines.MyComponent.Tests/
Why This Structure?
.Abstractions: Contains the contract (what the component does)
.Database: Contains the data model (entities and migrations)
Main project: Contains the implementation (how it does it)
.ApiClient: Auto-generated HTTP client for type-safe communication
Note: Database migrations are run by the centralized db-manager component.
This separation ensures clean boundaries and enables microservices architecture.
Your First Component: "Greeter"
Let's build a simple greeting service to learn the pattern.
Step 1: Create the Directory Structure
# From repository root
cd engines
mkdir -p greeter/src/Acsis.Dynaplex.Engines.Greeter.Abstractions/Models
mkdir -p greeter/src/Acsis.Dynaplex.Engines.Greeter
mkdir -p greeter/test/Acsis.Dynaplex.Engines.Greeter.Tests
Step 2: Define the Contract
Create the interface that defines what your component does.
File: engines/greeter/src/Acsis.Dynaplex.Engines.Greeter.Abstractions/IGreeterApi.cs
namespace Acsis.Dynaplex.Engines.Greeter.Abstractions;
/// <summary>
/// API for greeting users
/// </summary>
public interface IGreeterApi
{
/// <summary>
/// Gets a personalized greeting
/// </summary>
/// <param name="name">The person's name</param>
/// <returns>A greeting message</returns>
Task<string> GetGreetingAsync(string name);
/// <summary>
/// Gets greeting statistics
/// </summary>
/// <returns>Statistics about greetings sent</returns>
Task<GreetingStats> GetStatsAsync();
}
Step 3: Create a Model
Models live in .Abstractions because they're part of the contract.
File: engines/greeter/src/Acsis.Dynaplex.Engines.Greeter.Abstractions/Models/GreetingStats.cs
namespace Acsis.Dynaplex.Engines.Greeter.Abstractions.Models;
/// <summary>
/// Statistics about greetings
/// </summary>
public class GreetingStats
{
/// <summary>
/// Total greetings sent
/// </summary>
public int TotalGreetings { get; set; }
/// <summary>
/// Most common name greeted
/// </summary>
public string? MostPopularName { get; set; }
}
Step 4: Create the .Abstractions Project
File: engines/greeter/src/Acsis.Dynaplex.Engines.Greeter.Abstractions/Acsis.Dynaplex.Engines.Greeter.Abstractions.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Step 5: Implement the API
Now implement the interface in the main project.
File: engines/greeter/src/Acsis.Dynaplex.Engines.Greeter/GreeterApi.cs
using Acsis.Dynaplex.Engines.Greeter.Abstractions;
using Acsis.Dynaplex.Engines.Greeter.Abstractions.Models;
using Microsoft.Extensions.Logging;
namespace Acsis.Dynaplex.Engines.Greeter;
public class GreeterApi : IGreeterApi
{
private readonly ILogger<GreeterApi> _logger;
private static int _greetingCount = 0;
public GreeterApi(ILogger<GreeterApi> logger)
{
_logger = logger;
}
public Task<string> GetGreetingAsync(string name)
{
_greetingCount++;
_logger.LogInformation("Greeting {Name} (total: {Count})", name, _greetingCount);
return Task.FromResult($"Hello, {name}! Welcome to Dynaplex.");
}
public Task<GreetingStats> GetStatsAsync()
{
return Task.FromResult(new GreetingStats
{
TotalGreetings = _greetingCount,
MostPopularName = "World" // Simplified for tutorial
});
}
}
Step 6: Create the Implementation Project
File: engines/greeter/src/Acsis.Dynaplex.Engines.Greeter/Acsis.Dynaplex.Engines.Greeter.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(GreeterAbstractions)" />
</ItemGroup>
</Project>
Step 7: Register Your Component
Add to Directory.Build.props:
<PropertyGroup>
<GreeterAbstractions>$(EngineDir)greeter/src/Acsis.Dynaplex.Engines.Greeter.Abstractions/Acsis.Dynaplex.Engines.Greeter.Abstractions.csproj</GreeterAbstractions>
</PropertyGroup>
Step 8: Build and Test
# Build your component
dotnet build engines/greeter/src/Acsis.Dynaplex.Engines.Greeter.Abstractions/
dotnet build engines/greeter/src/Acsis.Dynaplex.Engines.Greeter/
# If successful, your component is ready!
What You've Learned
✅ Component structure - Four projects with clear purposes
✅ Contracts - Interfaces define what components do
✅ Models - Live in .Abstractions as part of the contract
✅ Implementation - Implements the interface
✅ Dependency injection - Constructor injection for logging
Next Steps
Now that you understand the basics:
- Create a complete component - Full step-by-step guide with ASP.NET Core service
- Learn component patterns - Best practices and advanced patterns
- Explore existing components - See real-world examples
Common Questions
Q: Why separate .Abstractions from implementation?
A: This enables microservices architecture where services communicate via HTTP. Each service only knows about other services' contracts, not their implementation.
Q: When do I use .ApiClient?
A: When your component needs to call another component's HTTP API. The client is auto-generated from the OpenAPI specification.
Q: Do all components need a database?
A: No! Only create a .Database project if your component stores data. If you do, register it with the db-manager.
Q: Can components reference each other?
A: Services can only reference other services' .Abstractions projects. At runtime, they communicate via HTTP using generated API clients.
Great job! You've built your first Dynaplex component. 🎉