Project Init
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="NetArchTest.Rules" Version="1.3.2" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Modules\ProjectManagement\ColaFlow.Modules.ProjectManagement.Domain\ColaFlow.Modules.ProjectManagement.Domain.csproj" />
|
||||
<ProjectReference Include="..\..\src\Modules\ProjectManagement\ColaFlow.Modules.ProjectManagement.Application\ColaFlow.Modules.ProjectManagement.Application.csproj" />
|
||||
<ProjectReference Include="..\..\src\Shared\ColaFlow.Shared.Kernel\ColaFlow.Shared.Kernel.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,175 @@
|
||||
using NetArchTest.Rules;
|
||||
using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate;
|
||||
using ColaFlow.Shared.Kernel.Common;
|
||||
|
||||
namespace ColaFlow.ArchitectureTests;
|
||||
|
||||
/// <summary>
|
||||
/// Architecture tests to enforce module boundaries and dependencies.
|
||||
/// These tests ensure the modular monolith architecture is maintained.
|
||||
/// </summary>
|
||||
public class ModuleBoundaryTests
|
||||
{
|
||||
private const string PM_DOMAIN = "ColaFlow.Modules.ProjectManagement.Domain";
|
||||
private const string PM_APPLICATION = "ColaFlow.Modules.ProjectManagement.Application";
|
||||
private const string PM_INFRASTRUCTURE = "ColaFlow.Modules.ProjectManagement.Infrastructure";
|
||||
private const string SHARED_KERNEL = "ColaFlow.Shared.Kernel";
|
||||
|
||||
[Fact]
|
||||
public void Domain_Should_Not_Depend_On_Application()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = typeof(Project).Assembly;
|
||||
|
||||
// Act
|
||||
var result = Types.InAssembly(assembly)
|
||||
.That()
|
||||
.ResideInNamespace(PM_DOMAIN)
|
||||
.ShouldNot()
|
||||
.HaveDependencyOn(PM_APPLICATION)
|
||||
.GetResult();
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccessful,
|
||||
$"Domain layer should not depend on Application layer. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Domain_Should_Not_Depend_On_Infrastructure()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = typeof(Project).Assembly;
|
||||
|
||||
// Act
|
||||
var result = Types.InAssembly(assembly)
|
||||
.That()
|
||||
.ResideInNamespace(PM_DOMAIN)
|
||||
.ShouldNot()
|
||||
.HaveDependencyOn(PM_INFRASTRUCTURE)
|
||||
.GetResult();
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccessful,
|
||||
$"Domain layer should not depend on Infrastructure layer. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Domain_Can_Only_Depend_On_Shared_Kernel()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = typeof(Project).Assembly;
|
||||
|
||||
// Act
|
||||
var result = Types.InAssembly(assembly)
|
||||
.That()
|
||||
.ResideInNamespace(PM_DOMAIN)
|
||||
.And()
|
||||
.HaveDependencyOnAny("ColaFlow")
|
||||
.Should()
|
||||
.HaveDependencyOn(SHARED_KERNEL)
|
||||
.Or()
|
||||
.ResideInNamespace(PM_DOMAIN)
|
||||
.GetResult();
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccessful,
|
||||
$"Domain layer should only depend on Shared.Kernel. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Application_Should_Not_Depend_On_Infrastructure()
|
||||
{
|
||||
// Arrange
|
||||
// Note: Application assembly might not have types yet, so we'll skip this test for now
|
||||
// This test will be enabled when Application layer is implemented
|
||||
|
||||
Assert.True(true, "Skipped - Application layer not yet implemented");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Project_Should_Be_AggregateRoot()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = typeof(Project).Assembly;
|
||||
|
||||
// Act
|
||||
var result = Types.InAssembly(assembly)
|
||||
.That()
|
||||
.HaveName("Project")
|
||||
.Should()
|
||||
.Inherit(typeof(AggregateRoot))
|
||||
.GetResult();
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccessful,
|
||||
$"Project should inherit from AggregateRoot. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Entities_Should_Inherit_From_Entity()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = typeof(Project).Assembly;
|
||||
|
||||
// Act
|
||||
var result = Types.InAssembly(assembly)
|
||||
.That()
|
||||
.ResideInNamespace($"{PM_DOMAIN}.Aggregates")
|
||||
.And()
|
||||
.AreClasses()
|
||||
.And()
|
||||
.AreNotAbstract()
|
||||
.Should()
|
||||
.Inherit(typeof(Entity))
|
||||
.GetResult();
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccessful,
|
||||
$"All entity classes should inherit from Entity. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Domain_Events_Should_Be_Records()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = typeof(Project).Assembly;
|
||||
|
||||
// Act
|
||||
var types = Types.InAssembly(assembly)
|
||||
.That()
|
||||
.ResideInNamespace($"{PM_DOMAIN}.Events")
|
||||
.And()
|
||||
.DoNotHaveName("DomainEvent") // Exclude base class
|
||||
.GetTypes();
|
||||
|
||||
// Assert - Check if types are records (records are sealed and inherit from a specific base)
|
||||
foreach (var type in types)
|
||||
{
|
||||
Assert.True(type.IsClass && type.IsSealed,
|
||||
$"Event {type.Name} should be a record (sealed class)");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValueObjects_Should_Be_Immutable()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = typeof(Project).Assembly;
|
||||
|
||||
// Act
|
||||
var result = Types.InAssembly(assembly)
|
||||
.That()
|
||||
.ResideInNamespace($"{PM_DOMAIN}.ValueObjects")
|
||||
.And()
|
||||
.AreClasses()
|
||||
.And()
|
||||
.AreNotAbstract()
|
||||
.Should()
|
||||
.BeSealed()
|
||||
.GetResult();
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccessful,
|
||||
$"All value objects should be sealed (immutable). Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user