Documentation
how-to/create-library.md
How to Create a Library
This guide provides step-by-step instructions for creating a Dynaplex library project—a pure, stateless code package that can be directly referenced by components.
Prerequisites
- Dynaplex repository cloned
- .NET 9 SDK installed
- Understanding of library vs component distinction
- Architecture team approval (libraries require review)
When to Create a Library
Before creating a library, verify your code meets ALL qualification criteria:
| Criterion | Your Code Must... |
|---|---|
| No persistence | Have no DbContext, no migrations |
| No endpoints | Have no Program.cs, no HTTP endpoints |
| Pure logic | Be deterministic, side-effect-free |
| Cross-cutting | Be used by ≥2 components |
| Stable semantics | Change rarely |
| No business workflows | Not implement domain policies |
If any criterion is not met, your code belongs in a component, not a library.
See ADR-046: Library Projects for complete criteria.
Step 1: Create Directory Structure
# From repository root
LIBRARY_NAME="my-utility" # Change to your library name (kebab-case)
LIBRARY_PASCAL="MyUtility" # PascalCase version
mkdir -p libraries/${LIBRARY_NAME}/src/Acsis.Dynaplex.Strata.${LIBRARY_PASCAL}
Example structure:
libraries/
└── my-utility/
└── src/
└── Acsis.Dynaplex.Strata.MyUtility/
├── Acsis.Dynaplex.Strata.MyUtility.csproj
└── [your code files]
Step 2: Create Project File
File: libraries/my-utility/src/Acsis.Dynaplex.Strata.MyUtility/Acsis.Dynaplex.Strata.MyUtility.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsLibrary>true</IsLibrary>
</PropertyGroup>
<!--
IMPORTANT: Libraries must have MINIMAL dependencies.
Only add packages that are truly necessary.
FORBIDDEN packages (will cause analyzer errors):
- Microsoft.EntityFrameworkCore.*
- Microsoft.AspNetCore.*
-->
<ItemGroup>
<!-- Example: Logging abstractions (if needed) -->
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
</ItemGroup>
<!--
Libraries can reference other libraries.
Libraries CANNOT reference:
- Acsis.Dynaplex.Engines.* (any component project)
- *.Database projects
- *.ApiClient projects
-->
<ItemGroup>
<!-- Example: Reference another library if needed -->
<!-- <ProjectReference Include="..\..\..\..\libraries\other-lib\src\Acsis.Dynaplex.Strata.OtherLib\Acsis.Dynaplex.Strata.OtherLib.csproj" /> -->
</ItemGroup>
</Project>
Step 3: Create Your Code
Libraries should follow these guidelines:
Use Static or Sealed Classes
namespace Acsis.Dynaplex.Strata.MyUtility;
/// <summary>
/// Provides utility methods for [describe purpose].
/// </summary>
public static class MyUtilityHelper {
/// <summary>
/// [Describe what this method does].
/// </summary>
/// <param name="input">The input to process.</param>
/// <returns>The processed result.</returns>
public static string Process(string input) {
ArgumentNullException.ThrowIfNull(input);
return input.ToUpperInvariant();
}
}
Use Immutable Types
namespace Acsis.Dynaplex.Strata.MyUtility;
/// <summary>
/// Represents a [describe the value object].
/// </summary>
public readonly record struct MyValueObject {
public required string Value { get; init; }
public static MyValueObject Create(string value) {
ArgumentException.ThrowIfNullOrWhiteSpace(value);
return new MyValueObject { Value = value };
}
}
Guidelines
- ✅ Use
staticclasses for utility methods - ✅ Use
sealedclasses unless inheritance is needed - ✅ Use
readonlyand immutability where possible - ✅ Add comprehensive XML documentation
- ✅ Validate inputs at public API boundaries
- ❌ Avoid mutable state
- ❌ Avoid I/O operations (file, network, database)
- ❌ Avoid environment/configuration access
Step 4: Add to Solution
dotnet sln acsis-core.slnx add \
libraries/my-utility/src/Acsis.Dynaplex.Strata.MyUtility/Acsis.Dynaplex.Strata.MyUtility.csproj
Step 5: Create Unit Tests
Create a test project for your library:
mkdir -p libraries/my-utility/test/Acsis.Dynaplex.Strata.MyUtility.Tests
File: libraries/my-utility/test/Acsis.Dynaplex.Strata.MyUtility.Tests/Acsis.Dynaplex.Strata.MyUtility.Tests.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Acsis.Dynaplex.Strata.MyUtility\Acsis.Dynaplex.Strata.MyUtility.csproj" />
</ItemGroup>
</Project>
File: libraries/my-utility/test/Acsis.Dynaplex.Strata.MyUtility.Tests/MyUtilityHelperTests.cs
namespace Acsis.Dynaplex.Strata.MyUtility.Tests;
public class MyUtilityHelperTests {
[Fact]
public void Process_WithValidInput_ReturnsUppercase() {
// Arrange
var input = "hello";
// Act
var result = MyUtilityHelper.Process(input);
// Assert
Assert.Equal("HELLO", result);
}
[Fact]
public void Process_WithNullInput_ThrowsArgumentNullException() {
// Act & Assert
Assert.Throws<ArgumentNullException>(() => MyUtilityHelper.Process(null!));
}
}
Add tests to solution:
dotnet sln acsis-core.slnx add \
libraries/my-utility/test/Acsis.Dynaplex.Strata.MyUtility.Tests/Acsis.Dynaplex.Strata.MyUtility.Tests.csproj
Step 6: Verify Build
# Build the library
dotnet build libraries/my-utility/src/Acsis.Dynaplex.Strata.MyUtility/
# Run tests
dotnet test libraries/my-utility/test/Acsis.Dynaplex.Strata.MyUtility.Tests/
Step 7: Use from Components
Components can now reference your library:
<!-- In a component's .csproj file -->
<ItemGroup>
<ProjectReference Include="..\..\..\..\libraries\my-utility\src\Acsis.Dynaplex.Strata.MyUtility\Acsis.Dynaplex.Strata.MyUtility.csproj" />
</ItemGroup>
Analyzer Enforcement
Libraries are automatically validated by Roslyn analyzers. If you violate library constraints, you'll see compile-time errors:
| Rule | Trigger | Solution |
|---|---|---|
| ACSIS0080 | Referencing component project | Move code to component, not library |
| ACSIS0081 | Using Entity Framework Core | Move to component's .Database project |
| ACSIS0082 | Using ASP.NET Core | Move to component service project |
| ACSIS0083 | Using HttpClient | Move to component service project |
| ACSIS0084 | Referencing .Database project |
Remove dependency or move to component |
| ACSIS0085 | Referencing .ApiClient project |
Remove dependency or move to component |
Library Checklist
Before considering your library complete:
Structure
- Located in
libraries/{name}/src/Acsis.Dynaplex.Strata.{Name}/ - Uses
Acsis.Dynaplex.Strata.*namespace - Added to development solution
Code Quality
- All classes are
staticorsealedwhere appropriate - Uses immutability (
readonly,init, records) - No mutable global state
- No I/O operations
Dependencies
- No EF Core packages
- No ASP.NET Core packages
- No component project references
- Minimal external dependencies
Documentation
- XML documentation on all public APIs
- README.md in library folder (optional)
Testing
- Unit tests for all public methods
- Tests pass:
dotnet test
Governance
- Architecture team approved
- Added to ADR-046 appendix (if significant)
Existing Libraries
Current libraries in the codebase:
| Library | Purpose |
|---|---|
Acsis.Dynaplex |
Core infrastructure utilities |
Acsis.Dynaplex.Strata.ServiceDefaults |
Aspire service configuration |
Acsis.Dynaplex.Strata.Orchestration |
Component registration helpers |
Acsis.EntLibCompatShim |
Enterprise Library compatibility |
Acsis.Encoding.Gs1 |
GS1 barcode encoding |
Note: These are legacy libraries that will eventually migrate to
Acsis.Dynaplex.Strata.*naming.
References
- ADR-046: Library Projects - Complete architectural decision
- Dynaplex Architecture - Library overview
- Component Patterns - When to extract to library