From 6140b750aa2331c4aeafd45c713817bc428b74fd Mon Sep 17 00:00:00 2001 From: wsuwala Date: Wed, 10 Dec 2025 07:37:01 +0100 Subject: [PATCH 1/4] upgrade to .net 10 and update dependencies --- .../DebtorRegistryMock.csproj | 2 +- .../Asserts/DomainEventsAssert.cs | 18 +++++------ .../Asserts/LoanApplicationAssert.cs | 10 +++--- .../LoanApplication.TacticalDdd.Tests.csproj | 12 +++---- .../LoanApplication.TacticalDdd.csproj | 17 +++++----- .../LoanApplication.TacticalDdd/Program.cs | 32 +++++++++++++------ 6 files changed, 53 insertions(+), 38 deletions(-) diff --git a/LoanApplication.TacticalDdd/DebtorRegistryMock/DebtorRegistryMock.csproj b/LoanApplication.TacticalDdd/DebtorRegistryMock/DebtorRegistryMock.csproj index 07842dd..1c69321 100644 --- a/LoanApplication.TacticalDdd/DebtorRegistryMock/DebtorRegistryMock.csproj +++ b/LoanApplication.TacticalDdd/DebtorRegistryMock/DebtorRegistryMock.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 disable enable diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/DomainEventsAssert.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/DomainEventsAssert.cs index 6b464c5..38275bb 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/DomainEventsAssert.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/DomainEventsAssert.cs @@ -1,6 +1,5 @@ using FluentAssertions; using FluentAssertions.Execution; -using FluentAssertions.Primitives; using LoanApplication.TacticalDdd.DomainModel.Ddd; namespace LoanApplication.TacticalDdd.Tests.Asserts; @@ -13,10 +12,12 @@ public static DomainEventsAssert Should(this IEnumerable events) } } -public class DomainEventsAssert : ReferenceTypeAssertions,DomainEventsAssert> +public class DomainEventsAssert { - public DomainEventsAssert(IEnumerable events) : base(events) + private readonly IEnumerable Subject; + public DomainEventsAssert(IEnumerable events) { + Subject = events; } public AndConstraint HaveExpectedNumberOfEvents(int expectedNumberOfEvents) @@ -27,12 +28,11 @@ public AndConstraint HaveExpectedNumberOfEvents(int expected public AndConstraint ContainEvent(Predicate matcher) where T : DomainEvent { - Execute.Assertion - .ForCondition(Subject.Any(e => e.GetType() == typeof(T) && matcher((T) e))) - .FailWith("List of events does not contain any that meets criteria"); - + using (new AssertionScope()) + { + Subject.Any(e => e.GetType() == typeof(T) && matcher((T)e)) + .Should().BeTrue("List of events does not contain any that meets criteria"); + } return new AndConstraint(this); } - - protected override string Identifier => "DomainEventsAssert"; } \ No newline at end of file diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/LoanApplicationAssert.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/LoanApplicationAssert.cs index 9d33ce0..1ae7cd3 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/LoanApplicationAssert.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/LoanApplicationAssert.cs @@ -10,12 +10,13 @@ public static LoanApplicationAssert Should(this DomainModel.LoanApplication loan => new LoanApplicationAssert(loanApplication); } -public class LoanApplicationAssert : ReferenceTypeAssertions +public class LoanApplicationAssert { + private readonly DomainModel.LoanApplication Subject; + public LoanApplicationAssert(DomainModel.LoanApplication loanApplication) - : base(loanApplication) { - + Subject = loanApplication; } public AndConstraint BeInStatus(LoanApplicationStatus expectedStatus) @@ -61,5 +62,6 @@ public AndConstraint HaveGreenScore() return ScoreIs(ApplicationScore.Green); } - protected override string Identifier => "LoanApplicationAssert"; + // kept for compatibility with previous naming in messages + private const string Identifier = "LoanApplicationAssert"; } \ No newline at end of file diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/LoanApplication.TacticalDdd.Tests.csproj b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/LoanApplication.TacticalDdd.Tests.csproj index 848621c..5769fd9 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/LoanApplication.TacticalDdd.Tests.csproj +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/LoanApplication.TacticalDdd.Tests.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 disable enable @@ -9,14 +9,14 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.csproj b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.csproj index 4fdac13..412cf5b 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.csproj +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.csproj @@ -1,22 +1,23 @@ - net9.0 + net10.0 disable enable - - + + - - - + + + - - + + + diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Program.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Program.cs index eebeb5a..39d93f8 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Program.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Program.cs @@ -7,7 +7,9 @@ using LoanApplication.TacticalDdd.Security; using LoanApplication.TacticalDdd.ReadModel; using Microsoft.AspNetCore.Authentication; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; + +// using Microsoft.OpenApi.Models; // removed to avoid dependency on Microsoft.OpenApi types var builder = WebApplication.CreateBuilder(); @@ -25,17 +27,17 @@ builder.Services.AddApplicationServices(); builder.Services.AddReadModelServices(builder.Configuration); builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(options => +builder.Services.AddSwaggerGen(opts => { - options.AddSecurityDefinition("basic", new OpenApiSecurityScheme + opts.AddSecurityDefinition("basic", new OpenApiSecurityScheme { - Name = "Authorization", - Type = SecuritySchemeType.Http, - Scheme = "basic", - In = ParameterLocation.Header, - Description = "Basic Auth" + Type = SecuritySchemeType.Http, + Scheme = "basic", + In = ParameterLocation.Header, + Name = "Authorization" }); - options.AddSecurityRequirement(new OpenApiSecurityRequirement + + /*opts.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme @@ -43,11 +45,21 @@ Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, - Id="basic" + Id = "basic" } }, Array.Empty() } + });*/ + opts.AddSecurityRequirement(doc => + { + return new OpenApiSecurityRequirement + { + { + new OpenApiSecuritySchemeReference("basic", doc), + [] + } + }; }); }); builder.Services.AddCarter(); From e3a76df8ee0f0b745605b5e582fe7eb3fcd6ed2f Mon Sep 17 00:00:00 2001 From: wsuwala Date: Thu, 30 Apr 2026 09:21:52 +0200 Subject: [PATCH 2/4] various code cleanups --- .../LoanApplicationDecisionServiceTests.cs | 17 ++++----- .../LoanApplicationEvaluationServiceTests.cs | 10 +++--- .../LoanApplicationSubmissionServiceTests.cs | 11 +++--- .../Asserts/DomainEventsAssert.cs | 12 ++----- .../Asserts/LoanApplicationAssert.cs | 17 +++------ .../Builders/LoanApplicationBuilder.cs | 2 +- .../Application/Api/LoanApplicationDto.cs | 6 ++-- .../Application/Api/LoanApplicationInfoDto.cs | 2 +- .../Api/LoanApplicationSubmissionDto.cs | 2 +- .../LoanApplicationEvaluationService.cs | 22 ++++-------- .../LoanApplicationSubmissionService.cs | 2 +- .../DomainModel/Registration.cs | 4 +-- .../DomainModel/ScoringResult.cs | 4 +-- .../DomainModel/ScoringRules.cs | 11 ++---- .../DataAccess/DbInitializer.cs | 36 ------------------- .../Infrastructure/DataAccess/EfUnitOfWork.cs | 5 +-- .../ExternalServices/DebtorRegistryClient.cs | 7 ++-- .../MessageQueue/RabbitMqEventPublisher.cs | 5 +-- .../LoanApplication.TacticalDdd/Program.cs | 16 --------- 19 files changed, 45 insertions(+), 146 deletions(-) delete mode 100644 LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/DbInitializer.cs diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationDecisionServiceTests.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationDecisionServiceTests.cs index 1417427..7f7e26f 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationDecisionServiceTests.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationDecisionServiceTests.cs @@ -20,8 +20,7 @@ public void LoanApplicationDecisionService_GreenApplication_CanBeAccepted() GivenOperator().WithLogin("admin").Build() }); - var existingApplications = new InMemoryLoanApplicationRepository(new [] - { + var existingApplications = new InMemoryLoanApplicationRepository([ GivenLoanApplication() .WithNumber("123") .WithCustomer(customer => customer.WithAge(25).WithIncome(15_000M)) @@ -29,7 +28,7 @@ public void LoanApplicationDecisionService_GreenApplication_CanBeAccepted() .WithProperty(prop => prop.WithValue(250_000M)) .Evaluated() .Build() - }); + ]); var eventBus = new InMemoryBus(); @@ -62,8 +61,7 @@ public void LoanApplicationDecisionService_GreenApplication_CanBeRejected() GivenOperator().WithLogin("admin").Build() }); - var existingApplications = new InMemoryLoanApplicationRepository(new [] - { + var existingApplications = new InMemoryLoanApplicationRepository([ GivenLoanApplication() .WithNumber("123") .WithCustomer(customer => customer.WithAge(25).WithIncome(15_000M)) @@ -71,7 +69,7 @@ public void LoanApplicationDecisionService_GreenApplication_CanBeRejected() .WithProperty(prop => prop.WithValue(250_000M)) .Evaluated() .Build() - }); + ]); var eventBus = new InMemoryBus(); @@ -98,9 +96,8 @@ public void LoanApplicationDecisionService_GreenApplication_CanBeRejected() private ClaimsPrincipal OperatorIdentity(string login) { - return new ClaimsPrincipal(new ClaimsIdentity(new Claim[] - { - new Claim(ClaimTypes.Name, login) - })); + return new ClaimsPrincipal(new ClaimsIdentity([ + new Claim(ClaimTypes.Name, login) + ])); } } \ No newline at end of file diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationEvaluationServiceTests.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationEvaluationServiceTests.cs index 457c1da..cfe14a6 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationEvaluationServiceTests.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationEvaluationServiceTests.cs @@ -12,15 +12,14 @@ public class LoanApplicationEvaluationServiceTests [Fact] public void LoanApplicationEvaluationService_ApplicationThatSatisfiesAllRules_IsEvaluatedGreen() { - var existingApplications = new InMemoryLoanApplicationRepository(new [] - { + var existingApplications = new InMemoryLoanApplicationRepository([ GivenLoanApplication() .WithNumber("123") .WithCustomer(customer => customer.WithAge(25).WithIncome(15_000M)) .WithLoan(loan => loan.WithAmount(200_000).WithNumberOfYears(25).WithInterestRate(1.1M)) .WithProperty(prop => prop.WithValue(250_000M)) .Build() - }); + ]); var evaluationService = new LoanApplicationEvaluationService ( @@ -39,15 +38,14 @@ public void LoanApplicationEvaluationService_ApplicationThatSatisfiesAllRules_Is [Fact] public void LoanApplicationEvaluationService_ApplicationThatDoesNotSatisfyAllRules_IsEvaluatedRedAndRejected() { - var existingApplications = new InMemoryLoanApplicationRepository(new [] - { + var existingApplications = new InMemoryLoanApplicationRepository([ GivenLoanApplication() .WithNumber("123") .WithCustomer(customer => customer.WithAge(55).WithIncome(15_000M)) .WithLoan(loan => loan.WithAmount(200_000).WithNumberOfYears(25).WithInterestRate(1.1M)) .WithProperty(prop => prop.WithValue(250_000M)) .Build() - }); + ]); var evaluationService = new LoanApplicationEvaluationService ( diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationSubmissionServiceTests.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationSubmissionServiceTests.cs index 1fb7a6d..3da86e9 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationSubmissionServiceTests.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/ApplicationTests/LoanApplicationSubmissionServiceTests.cs @@ -35,7 +35,7 @@ public void LoanApplicationSubmissionService_ValidApplication_GetsSubmitted() CustomerNationalIdentifier : "11111111119", CustomerFirstName : "Frank", CustomerLastName : "Oz", - CustomerBirthdate : SysTime.Now().AddYears(-25), + CustomerBirthdate : SysTime.Today().AddYears(-25), CustomerMonthlyIncome : 10_000M, CustomerAddress : new AddressDto ( @@ -87,7 +87,7 @@ public void LoanApplicationSubmissionService_InvalidApplication_IsNotSaved() CustomerNationalIdentifier : "11111111119111", CustomerFirstName : "Frank", CustomerLastName : "Oz", - CustomerBirthdate : SysTime.Now().AddYears(-25), + CustomerBirthdate : SysTime.Today().AddYears(-25), CustomerMonthlyIncome : 10_000M, CustomerAddress : new AddressDto ( @@ -120,9 +120,8 @@ public void LoanApplicationSubmissionService_InvalidApplication_IsNotSaved() private ClaimsPrincipal OperatorIdentity(string login) { - return new ClaimsPrincipal(new ClaimsIdentity(new Claim[] - { - new Claim(ClaimTypes.Name, login) - })); + return new ClaimsPrincipal(new ClaimsIdentity([ + new Claim(ClaimTypes.Name, login) + ])); } } \ No newline at end of file diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/DomainEventsAssert.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/DomainEventsAssert.cs index 38275bb..1de4612 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/DomainEventsAssert.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/DomainEventsAssert.cs @@ -12,17 +12,11 @@ public static DomainEventsAssert Should(this IEnumerable events) } } -public class DomainEventsAssert +public class DomainEventsAssert(IEnumerable events) { - private readonly IEnumerable Subject; - public DomainEventsAssert(IEnumerable events) - { - Subject = events; - } - public AndConstraint HaveExpectedNumberOfEvents(int expectedNumberOfEvents) { - Subject.Count().Should().Be(expectedNumberOfEvents); + events.Count().Should().Be(expectedNumberOfEvents); return new AndConstraint(this); } @@ -30,7 +24,7 @@ public AndConstraint ContainEvent(Predicate matcher) w { using (new AssertionScope()) { - Subject.Any(e => e.GetType() == typeof(T) && matcher((T)e)) + events.Any(e => e.GetType() == typeof(T) && matcher((T)e)) .Should().BeTrue("List of events does not contain any that meets criteria"); } return new AndConstraint(this); diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/LoanApplicationAssert.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/LoanApplicationAssert.cs index 1ae7cd3..2d33f20 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/LoanApplicationAssert.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Asserts/LoanApplicationAssert.cs @@ -10,18 +10,11 @@ public static LoanApplicationAssert Should(this DomainModel.LoanApplication loan => new LoanApplicationAssert(loanApplication); } -public class LoanApplicationAssert +public class LoanApplicationAssert(DomainModel.LoanApplication loanApplication) { - private readonly DomainModel.LoanApplication Subject; - - public LoanApplicationAssert(DomainModel.LoanApplication loanApplication) - { - Subject = loanApplication; - } - public AndConstraint BeInStatus(LoanApplicationStatus expectedStatus) { - Subject.Status.Should().Be(expectedStatus); + loanApplication.Status.Should().Be(expectedStatus); return new AndConstraint(this); } @@ -42,13 +35,13 @@ public AndConstraint BeNew() public AndConstraint ScoreIsNull() { - Subject.Score.Should().BeNull(); + loanApplication.Score.Should().BeNull(); return new AndConstraint(this); } public AndConstraint ScoreIs(ApplicationScore expectedScore) { - Subject.Score?.Score.Should().Be(expectedScore); + loanApplication.Score?.Score.Should().Be(expectedScore); return new AndConstraint(this); } @@ -62,6 +55,4 @@ public AndConstraint HaveGreenScore() return ScoreIs(ApplicationScore.Green); } - // kept for compatibility with previous naming in messages - private const string Identifier = "LoanApplicationAssert"; } \ No newline at end of file diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Builders/LoanApplicationBuilder.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Builders/LoanApplicationBuilder.cs index 3e06648..26d2066 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Builders/LoanApplicationBuilder.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd.Tests/Builders/LoanApplicationBuilder.cs @@ -12,7 +12,7 @@ public class LoanApplicationBuilder private LoanApplicationNumber applicationNumber = new LoanApplicationNumber(Guid.NewGuid().ToString()); private bool evaluated = false; private LoanApplicationStatus targetStatus = LoanApplicationStatus.New; - private ScoringRulesFactory scoringRulesFactory = new ScoringRulesFactory(new DebtorRegistryMock()); + private readonly ScoringRulesFactory scoringRulesFactory = new ScoringRulesFactory(new DebtorRegistryMock()); public static LoanApplicationBuilder GivenLoanApplication() => new LoanApplicationBuilder(); public LoanApplicationBuilder Accepted() diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationDto.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationDto.cs index 1de2289..0b9ea08 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationDto.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationDto.cs @@ -8,7 +8,7 @@ public record LoanApplicationDto string CustomerNationalIdentifier, string CustomerFirstName, string CustomerLastName, - DateTime CustomerBirthdate, + DateOnly CustomerBirthdate, decimal CustomerMonthlyIncome, AddressDto CustomerAddress, decimal PropertyValue, @@ -16,10 +16,10 @@ public record LoanApplicationDto decimal LoanAmount, int LoanNumberOfYears, decimal InterestRate, - DateTime? DecisionDate, + DateOnly? DecisionDate, string DecisionBy, string RegisteredBy, - DateTime RegistrationDate + DateOnly RegistrationDate ) { //this one is needed to allow dapper to create instance of it using reflection diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationInfoDto.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationInfoDto.cs index 9e75de4..6cc95ef 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationInfoDto.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationInfoDto.cs @@ -5,7 +5,7 @@ public record LoanApplicationInfoDto string Number, string Status, string CustomerName, - DateTime? DecisionDate, + DateOnly? DecisionDate, decimal LoanAmount, string DecisionBy ); \ No newline at end of file diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationSubmissionDto.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationSubmissionDto.cs index 24a5a74..b58af41 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationSubmissionDto.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/Api/LoanApplicationSubmissionDto.cs @@ -7,7 +7,7 @@ public record LoanApplicationSubmissionDto string CustomerNationalIdentifier, string CustomerFirstName, string CustomerLastName, - DateTime CustomerBirthdate, + DateOnly CustomerBirthdate, decimal CustomerMonthlyIncome, AddressDto CustomerAddress, decimal PropertyValue, diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/LoanApplicationEvaluationService.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/LoanApplicationEvaluationService.cs index 393a9dd..5e53bdd 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/LoanApplicationEvaluationService.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/LoanApplicationEvaluationService.cs @@ -3,23 +3,13 @@ namespace LoanApplication.TacticalDdd.Application; using DomainModel; using DomainModel.Ddd; -public class LoanApplicationEvaluationService +public class LoanApplicationEvaluationService( + IUnitOfWork unitOfWork, + ILoanApplicationRepository loanApplications, + IDebtorRegistry debtorRegistry) { - private readonly IUnitOfWork unitOfWork; - private readonly ILoanApplicationRepository loanApplications; - private readonly ScoringRulesFactory scoringRulesFactory; - - public LoanApplicationEvaluationService - ( - IUnitOfWork unitOfWork, - ILoanApplicationRepository loanApplications, - IDebtorRegistry debtorRegistry - ) - { - this.unitOfWork = unitOfWork; - this.loanApplications = loanApplications; - this.scoringRulesFactory = new ScoringRulesFactory(debtorRegistry); - } + private readonly ScoringRulesFactory scoringRulesFactory = new(debtorRegistry); + public void EvaluateLoanApplication(string applicationNumber) { var loanApplication = loanApplications.WithNumber(LoanApplicationNumber.Of(applicationNumber)); diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/LoanApplicationSubmissionService.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/LoanApplicationSubmissionService.cs index 3afdf25..81357f7 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/LoanApplicationSubmissionService.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Application/LoanApplicationSubmissionService.cs @@ -18,7 +18,7 @@ public string SubmitLoanApplication(LoanApplicationSubmissionDto loanApplication ( new NationalIdentifier(loanApplicationDto.CustomerNationalIdentifier), new Name(loanApplicationDto.CustomerFirstName, loanApplicationDto.CustomerLastName), - DateOnly.FromDateTime(loanApplicationDto.CustomerBirthdate), + loanApplicationDto.CustomerBirthdate, new MonetaryAmount(loanApplicationDto.CustomerMonthlyIncome), new Address ( diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/Registration.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/Registration.cs index 5f39f9c..ca292a8 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/Registration.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/Registration.cs @@ -1,5 +1,4 @@ using LoanApplication.TacticalDdd.DomainModel.Ddd; -using Newtonsoft.Json; namespace LoanApplication.TacticalDdd.DomainModel; @@ -14,8 +13,7 @@ public Registration(DateOnly registrationDate, Operator registeredBy) { } - [JsonConstructor] - public Registration(DateOnly registrationDate, OperatorId registeredBy) + private Registration(DateOnly registrationDate, OperatorId registeredBy) { RegistrationDate = registrationDate; RegisteredBy = registeredBy; diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringResult.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringResult.cs index 619a1b4..90e78df 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringResult.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringResult.cs @@ -1,5 +1,4 @@ using LoanApplication.TacticalDdd.DomainModel.Ddd; -using Newtonsoft.Json; namespace LoanApplication.TacticalDdd.DomainModel; @@ -8,7 +7,6 @@ public class ScoringResult : ValueObject public ApplicationScore? Score { get; } public string Explanation { get; } - [JsonConstructor] private ScoringResult(ApplicationScore? score, string explanation) { Score = score; @@ -34,5 +32,5 @@ public static ScoringResult Red(string[] messages) => new (ApplicationScore.Red, string.Join(Environment.NewLine,messages)); - public bool IsRed() =>Score == ApplicationScore.Red; + public bool IsRed() => Score == ApplicationScore.Red; } \ No newline at end of file diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringRules.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringRules.cs index 65df90b..1bb8ad6 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringRules.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringRules.cs @@ -8,7 +8,7 @@ public ScoringResult Evaluate(LoanApplication loanApplication) .Where(r => !r.IsSatisfiedBy(loanApplication)) .ToList(); - return brokenRules.Any() ? + return brokenRules.Count != 0 ? ScoringResult.Red(brokenRules.Select(r=>r.Message).ToArray()) : ScoringResult.Green(); } } @@ -52,15 +52,8 @@ public bool IsSatisfiedBy(LoanApplication loanApplication) public string Message => "Installment is higher than 15% of customer's income."; } -public class CustomerIsNotARegisteredDebtor : IScoringRule +public class CustomerIsNotARegisteredDebtor(IDebtorRegistry debtorRegistry) : IScoringRule { - private readonly IDebtorRegistry debtorRegistry; - - public CustomerIsNotARegisteredDebtor(IDebtorRegistry debtorRegistry) - { - this.debtorRegistry = debtorRegistry; - } - public bool IsSatisfiedBy(LoanApplication loanApplication) { return !debtorRegistry.IsRegisteredDebtor(loanApplication.Customer); diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/DbInitializer.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/DbInitializer.cs deleted file mode 100644 index 1f2f1a5..0000000 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/DbInitializer.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace LoanApplication.TacticalDdd.Infrastructure.DataAccess; - -/* -public class DbInitializer : IHostedService -{ - private readonly IServiceProvider serviceProvider; - - public DbInitializer(IServiceProvider serviceProvider) - { - this.serviceProvider = serviceProvider; - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - using var scope = serviceProvider.CreateScope(); - - using var session = scope.ServiceProvider.GetService().LightweightSession(); - - if (!session.Query().Any(o=>o.Login=="admin")) - { - session.Insert(new Operator - ( - new Login("admin"), - new Password("admin"), - new Name("admin","admin"), - new MonetaryAmount(1_000_000M) - )); - - } - - await session.SaveChangesAsync(cancellationToken); - } - - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; -} -*/ \ No newline at end of file diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/EfUnitOfWork.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/EfUnitOfWork.cs index f24157d..172675d 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/EfUnitOfWork.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/EfUnitOfWork.cs @@ -4,8 +4,5 @@ namespace LoanApplication.TacticalDdd.Infrastructure.DataAccess; public class EfUnitOfWork(LoanDbContext dbContext) : IUnitOfWork { - public void CommitChanges() - { - dbContext.SaveChanges(); - } + public void CommitChanges() => dbContext.SaveChanges(); } \ No newline at end of file diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/ExternalServices/DebtorRegistryClient.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/ExternalServices/DebtorRegistryClient.cs index ccfca60..a7e252a 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/ExternalServices/DebtorRegistryClient.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/ExternalServices/DebtorRegistryClient.cs @@ -21,8 +21,7 @@ public class Debt public class DebtorRegistryClient { - public async Task GetDebtorInfo(string pesel) - { - return await RestClient.For("http://localhost:5005/DebtorInfo").Get(pesel); - } + public async Task GetDebtorInfo(string pesel) => + await RestClient.For("http://localhost:5005/DebtorInfo").Get(pesel); + } \ No newline at end of file diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/MessageQueue/RabbitMqEventPublisher.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/MessageQueue/RabbitMqEventPublisher.cs index 9a83893..2a82359 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/MessageQueue/RabbitMqEventPublisher.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/MessageQueue/RabbitMqEventPublisher.cs @@ -5,8 +5,5 @@ namespace LoanApplication.TacticalDdd.Infrastructure.MessageQueue; public class RabbitMqEventPublisher(IBus bus) : IEventPublisher { - public void Publish(DomainEvent @event) - { - bus.PubSub.Publish(@event); - } + public void Publish(DomainEvent @event) =>bus.PubSub.Publish(@event); } \ No newline at end of file diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Program.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Program.cs index 39d93f8..86e71e3 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Program.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Program.cs @@ -9,8 +9,6 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.OpenApi; -// using Microsoft.OpenApi.Models; // removed to avoid dependency on Microsoft.OpenApi types - var builder = WebApplication.CreateBuilder(); builder.Services @@ -37,20 +35,6 @@ Name = "Authorization" }); - /*opts.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "basic" - } - }, - Array.Empty() - } - });*/ opts.AddSecurityRequirement(doc => { return new OpenApiSecurityRequirement From d5f3d09ce9a0f16909dc38808e08ffb25d1265fc Mon Sep 17 00:00:00 2001 From: wsuwala Date: Thu, 30 Apr 2026 10:18:22 +0200 Subject: [PATCH 3/4] switch to use complex types instead of owned entities for VO --- .../DomainModel/ScoringResult.cs | 4 +- .../DataAccess/LoanDbContext.cs | 60 ++++++++++--------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringResult.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringResult.cs index 90e78df..05a3ce5 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringResult.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/DomainModel/ScoringResult.cs @@ -4,10 +4,10 @@ namespace LoanApplication.TacticalDdd.DomainModel; public class ScoringResult : ValueObject { - public ApplicationScore? Score { get; } + public ApplicationScore Score { get; } public string Explanation { get; } - private ScoringResult(ApplicationScore? score, string explanation) + private ScoringResult(ApplicationScore score, string explanation) { Score = score; Explanation = explanation; diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/LoanDbContext.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/LoanDbContext.cs index 3eee6e5..97990c6 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/LoanDbContext.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Infrastructure/DataAccess/LoanDbContext.cs @@ -32,21 +32,24 @@ public void Configure(EntityTypeBuilder builder) .HasConversion(x => x.Value, x => new OperatorId(x)); builder.Property(x => x.Login) - .HasConversion(x => x.Value, x => new Login(x)); + .HasConversion(x => x.Value, x => new Login(x)) + .IsRequired(); builder.Property(x => x.Password) - .HasConversion(x => x.Value, x => new Password(x)); + .HasConversion(x => x.Value, x => new Password(x)) + .IsRequired(); - builder.OwnsOne(x => x.Name, opts => + builder.ComplexProperty(x => x.Name, opts => { - opts.Property(x => x.First).HasColumnName("FirstName"); - opts.Property(x => x.Last).HasColumnName("LastName"); - }).Navigation(x => x.Name).IsRequired(); + opts.Property(x => x.First).HasColumnName("FirstName").IsRequired(); + opts.Property(x => x.Last).HasColumnName("LastName").IsRequired(); + }); builder.Property(x => x.CompetenceLevel) .HasConversion(x => x != null ? x.Amount : (decimal?)null, x => x.HasValue ? new MonetaryAmount(x.Value) : null) - .HasColumnName("CompetenceLevel_Amount"); + .HasColumnName("CompetenceLevel_Amount") + .IsRequired(); } } @@ -69,25 +72,24 @@ public void Configure(EntityTypeBuilder builder) .Property(x => x.Status) .HasConversion(); - builder.OwnsOne(x => x.Score, opts => + builder.ComplexProperty(x => x.Score, opts => { opts.Property(x => x.Explanation); - opts.Property(x => x.Score).HasConversion(); + opts.Property(x => x.Score).HasConversion().IsRequired(); }); - builder.OwnsOne(x => x.Customer, opts => + builder.ComplexProperty(x => x.Customer, opts => { opts .Property(x => x.NationalIdentifier) .HasConversion(x => x.Value, x => new NationalIdentifier(x)) - .HasColumnName("Customer_NationalIdentifier_Value") - .IsRequired(); + .HasColumnName("Customer_NationalIdentifier_Value"); - opts.OwnsOne(x => x.Name, name => + opts.ComplexProperty(x => x.Name, name => { name.Property(x => x.First).IsRequired(); name.Property(x => x.Last).IsRequired(); - }).Navigation(x=>x.Name).IsRequired(); + }); opts.Property(x => x.Birthdate).IsRequired(); @@ -96,32 +98,32 @@ public void Configure(EntityTypeBuilder builder) .HasConversion(x => x.Amount, x => new MonetaryAmount(x)) .HasColumnName("Customer_MonthlyIncome_Amount"); - opts.OwnsOne(x => x.Address, addr => + opts.ComplexProperty(x => x.Address, addr => { addr.Property(x => x.Country).IsRequired(); addr.Property(x => x.ZipCode).IsRequired(); addr.Property(x => x.City).IsRequired(); addr.Property(x => x.Street).IsRequired(); - }).Navigation(x => x.Address).IsRequired(); - - }).Navigation(x=>x.Customer).IsRequired(); + }); + }); - builder.OwnsOne(x => x.Property, opts => + builder.ComplexProperty(x => x.Property, opts => { opts.Property(x => x.Value) .HasConversion(x => x.Amount, x => new MonetaryAmount(x)) - .HasColumnName("Property_Value_Amount"); + .HasColumnName("Property_Value_Amount") + .IsRequired(); - opts.OwnsOne(x => x.Address, addr => + opts.ComplexProperty(x => x.Address, addr => { addr.Property(x => x.Country).IsRequired(); addr.Property(x => x.ZipCode).IsRequired(); addr.Property(x => x.City).IsRequired(); addr.Property(x => x.Street).IsRequired(); - }).Navigation(x => x.Address).IsRequired(); - }).Navigation(x => x.Property).IsRequired(); + }); + }); - builder.OwnsOne(x => x.Loan, opts => + builder.ComplexProperty(x => x.Loan, opts => { opts.Property(x => x.InterestRate) .HasConversion(x => x.Value, x => new Percent(x)) @@ -134,19 +136,19 @@ public void Configure(EntityTypeBuilder builder) .IsRequired(); opts.Property(x => x.LoanNumberOfYears).IsRequired(); - }).Navigation(x => x.Loan).IsRequired(); + }); - builder.OwnsOne(x => x.Decision, opts => + builder.ComplexProperty(x => x.Decision, opts => { - opts.Property(x => x.DecisionDate); + opts.Property(x => x.DecisionDate).IsRequired(); opts.Property(x => x.DecisionBy) .HasConversion(x => x != null ? x.Value : (Guid?)null, x => x.HasValue ? new OperatorId(x.Value) : null) .HasColumnName("Decision_DecisionBy_Value"); }); - builder.OwnsOne(x => x.Registration, opts => + builder.ComplexProperty(x => x.Registration, opts => { - opts.Property(x => x.RegistrationDate); + opts.Property(x => x.RegistrationDate).IsRequired(); opts.Property(x => x.RegisteredBy) .HasConversion(x => x != null ? x.Value : (Guid?)null, x => x.HasValue ? new OperatorId(x.Value) : null) .HasColumnName("Registration_RegisteredBy_Value"); From cd161e8a49ed6eafb93e266668283f99e4aeac0f Mon Sep 17 00:00:00 2001 From: wsuwala Date: Thu, 30 Apr 2026 10:48:43 +0200 Subject: [PATCH 4/4] snmall api enhancements --- .../Web/LoanApplicationApi.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Web/LoanApplicationApi.cs b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Web/LoanApplicationApi.cs index 8e13bc0..59e14ea 100644 --- a/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Web/LoanApplicationApi.cs +++ b/LoanApplication.TacticalDdd/LoanApplication.TacticalDdd/Web/LoanApplicationApi.cs @@ -3,10 +3,11 @@ using LoanApplication.TacticalDdd.Application.Api; using LoanApplication.TacticalDdd.ReadModel; using O9d.AspNet.FluentValidation; +using static Microsoft.AspNetCore.Http.StatusCodes; +using Carter; namespace LoanApplication.TacticalDdd.Web; -using Carter; public class LoanApplicationApi : ICarterModule { @@ -31,7 +32,7 @@ public void AddRoutes(IEndpointRouteBuilder app) loanApplicationEvaluationService.EvaluateLoanApplication(applicationNumber); return Results.Ok(); }) - .Produces(200); + .Produces(Status200OK); group .MapPut("accept/{applicationNumber}", (string applicationNumber, ClaimsPrincipal user, LoanApplicationDecisionService loanApplicationDecisionService) => @@ -39,7 +40,7 @@ public void AddRoutes(IEndpointRouteBuilder app) loanApplicationDecisionService.AcceptApplication(applicationNumber,user); return Results.Ok(); }) - .Produces(200); + .Produces(Status200OK); group @@ -48,7 +49,7 @@ public void AddRoutes(IEndpointRouteBuilder app) loanApplicationDecisionService.RejectApplication(applicationNumber,user, null); return Results.Ok(); }) - .Produces(200); + .Produces(Status200OK); group