From d5505a2201be6ba688b1a951418287fa284c0577 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 20:36:18 +0000 Subject: [PATCH] Refactor domain aggregates into separate folders and consolidate ValueObjects Reorganized Domain layer structure following DDD best practices: - Moved Expense, Income, and Payment to their own Aggregate folders - Consolidated ValueObjects (MoneyOwed, MoneyPaid, Email, Password, PersonName) into ValueObjects folder - Updated all namespace references across Domain, Infrastructure, Application, and Test layers - All builds and tests pass successfully Fixes #15 Co-Authored-By: Aaron Prohaska --- .../UpsertBudgets/UpsertBudgetHandler.cs | 2 + .../Mapping/MapsterConfiguration.cs | 3 ++ .../Aggregates/Accounts/Account.cs | 4 +- .../Aggregates/Accounts/Email.cs | 43 ------------------- .../Aggregates/Accounts/Password.cs | 31 ------------- .../Aggregates/Budgets/Budget.cs | 2 + .../Aggregates/Budgets/BudgetCalculator.cs | 1 + .../Aggregates/Budgets/ExpenseId.cs | 8 ---- .../Budgets/ExpensePaymentCreated.cs | 9 ++-- .../Aggregates/Budgets/Payment.cs | 41 ------------------ .../Aggregates/Budgets/PaymentId.cs | 13 ------ .../Aggregates/Debts/Debt.cs | 2 +- .../Debts/DebtPaymentCreatedEvent.cs | 7 +-- .../Aggregates/Expenses/.gitkeep | 0 .../{Budgets => Expenses}/Expense.cs | 5 ++- .../Aggregates/Expenses/ExpenseId.cs | 8 ++++ .../Aggregates/Incomes/.gitkeep | 0 .../Aggregates/{Budgets => Incomes}/Income.cs | 4 +- .../{Budgets => Incomes}/IncomeId.cs | 4 +- .../Aggregates/Payments/.gitkeep | 0 .../Aggregates/Payments/Payment.cs | 39 +++++++++++++++++ .../Aggregates/Payments/PaymentId.cs | 8 ++++ .../ValueObjects/Email.cs | 42 ++++++++++++++++++ .../Debts => ValueObjects}/MoneyOwed.cs | 2 +- .../Debts => ValueObjects}/MoneyPaid.cs | 2 +- .../ValueObjects/Password.cs | 31 +++++++++++++ .../Accounts => ValueObjects}/PersonName.cs | 2 +- .../EntityFramework/BudgetContext.cs | 3 ++ .../StronglyTypedIdConverters.cs | 3 ++ .../BudgetModelTests.cs | 2 + 30 files changed, 162 insertions(+), 159 deletions(-) delete mode 100644 src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Email.cs delete mode 100644 src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Password.cs delete mode 100644 src/Domain/BlazingBudget.Domain/Aggregates/Budgets/ExpenseId.cs delete mode 100644 src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Payment.cs delete mode 100644 src/Domain/BlazingBudget.Domain/Aggregates/Budgets/PaymentId.cs create mode 100644 src/Domain/BlazingBudget.Domain/Aggregates/Expenses/.gitkeep rename src/Domain/BlazingBudget.Domain/Aggregates/{Budgets => Expenses}/Expense.cs (85%) create mode 100644 src/Domain/BlazingBudget.Domain/Aggregates/Expenses/ExpenseId.cs create mode 100644 src/Domain/BlazingBudget.Domain/Aggregates/Incomes/.gitkeep rename src/Domain/BlazingBudget.Domain/Aggregates/{Budgets => Incomes}/Income.cs (89%) rename src/Domain/BlazingBudget.Domain/Aggregates/{Budgets => Incomes}/IncomeId.cs (61%) create mode 100644 src/Domain/BlazingBudget.Domain/Aggregates/Payments/.gitkeep create mode 100644 src/Domain/BlazingBudget.Domain/Aggregates/Payments/Payment.cs create mode 100644 src/Domain/BlazingBudget.Domain/Aggregates/Payments/PaymentId.cs create mode 100644 src/Domain/BlazingBudget.Domain/ValueObjects/Email.cs rename src/Domain/BlazingBudget.Domain/{Aggregates/Debts => ValueObjects}/MoneyOwed.cs (95%) rename src/Domain/BlazingBudget.Domain/{Aggregates/Debts => ValueObjects}/MoneyPaid.cs (95%) create mode 100644 src/Domain/BlazingBudget.Domain/ValueObjects/Password.cs rename src/Domain/BlazingBudget.Domain/{Aggregates/Accounts => ValueObjects}/PersonName.cs (95%) diff --git a/src/Application/BlazingBudget.Application/Budgets/UpsertBudgets/UpsertBudgetHandler.cs b/src/Application/BlazingBudget.Application/Budgets/UpsertBudgets/UpsertBudgetHandler.cs index b7d2b23..7a05254 100644 --- a/src/Application/BlazingBudget.Application/Budgets/UpsertBudgets/UpsertBudgetHandler.cs +++ b/src/Application/BlazingBudget.Application/Budgets/UpsertBudgets/UpsertBudgetHandler.cs @@ -1,5 +1,7 @@ using BlazingBudget.Domain.Aggregates.Accounts; using BlazingBudget.Domain.Aggregates.Budgets; +using BlazingBudget.Domain.Aggregates.Expenses; +using BlazingBudget.Domain.Aggregates.Incomes; using BlazingBudget.Domain.ValueObjects; using BlazingBudget.Infrastructure.Persistence.EntityFramework; using BlazingBudget.Infrastructure.Persistence.Extensions; diff --git a/src/Application/BlazingBudget.Application/Mapping/MapsterConfiguration.cs b/src/Application/BlazingBudget.Application/Mapping/MapsterConfiguration.cs index 51fa577..8dd0247 100644 --- a/src/Application/BlazingBudget.Application/Mapping/MapsterConfiguration.cs +++ b/src/Application/BlazingBudget.Application/Mapping/MapsterConfiguration.cs @@ -1,6 +1,9 @@ using BlazingBudget.Domain.Aggregates.Accounts; using BlazingBudget.Domain.Aggregates.Budgets; using BlazingBudget.Domain.Aggregates.Debts; +using BlazingBudget.Domain.Aggregates.Expenses; +using BlazingBudget.Domain.Aggregates.Incomes; +using BlazingBudget.Domain.Aggregates.Payments; using Mapster; namespace BlazingBudget.Application.Mapping; diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Account.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Account.cs index 61e9611..a4d33d1 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Account.cs +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Account.cs @@ -1,4 +1,6 @@ -namespace BlazingBudget.Domain.Aggregates.Accounts; +using BlazingBudget.Domain.ValueObjects; + +namespace BlazingBudget.Domain.Aggregates.Accounts; public sealed class Account : Entity { public PersonName Name { get; private set; } diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Email.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Email.cs deleted file mode 100644 index cd445a2..0000000 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Email.cs +++ /dev/null @@ -1,43 +0,0 @@ -using CSharpFunctionalExtensions; -using BlazingBudget.Domain.Common; - -namespace BlazingBudget.Domain.Aggregates.Accounts -{ - public class Email : ValueObject - { - public string Value { get; private set; } - - public const int MaxLength = 256; - - private Email() { } - - [JsonConstructor] - private Email(string value) - { - Value = value; - } - - public static Result Create(string input) - { - // TODO: How do we reuse these validation rules? - - if (string.IsNullOrWhiteSpace(input)) - return Result.Failure(Messages.Strings.ValueIsRequired); - - string email = input.Trim(); - - if (email.Length > MaxLength) - return Result.Failure(Messages.Strings.ValueIsTooLong); - - if (!Regex.IsMatch(email, @"^(.+)@(.+)$")) - return Result.Failure(Messages.Strings.ValueIsInvalid); - - return Result.Success(new Email(email)); - } - - protected override IEnumerable GetEqualityComponents() - { - yield return Value; - } - } -} diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Password.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Password.cs deleted file mode 100644 index a71d560..0000000 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/Password.cs +++ /dev/null @@ -1,31 +0,0 @@ -using CSharpFunctionalExtensions; - -namespace BlazingBudget.Domain.Aggregates.Accounts; - - public class Password : ValueObject - { - public string Value { get; private set; } - - private Password() { } - - [JsonConstructor] - private Password(string value) - { - Value = value; - } - - public static Result Create(string value) - { - if (Regex.Match(value, "(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9])(?=.{8,})").Success == false) - { - return Result.Failure("The password does not meet the required strength."); - } - - return Result.Success(new Password(value)); - } - - protected override IEnumerable GetEqualityComponents() - { - yield return Value; - } -} diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Budget.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Budget.cs index b56b871..1340f6c 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Budget.cs +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Budget.cs @@ -1,4 +1,6 @@ using BlazingBudget.Domain.Aggregates.Accounts; +using BlazingBudget.Domain.Aggregates.Expenses; +using BlazingBudget.Domain.Aggregates.Incomes; namespace BlazingBudget.Domain.Aggregates.Budgets; public sealed class Budget : Entity diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/BudgetCalculator.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/BudgetCalculator.cs index 5e677e8..f5f8b89 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/BudgetCalculator.cs +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/BudgetCalculator.cs @@ -1,3 +1,4 @@ +using BlazingBudget.Domain.Aggregates.Expenses; using CSharpFunctionalExtensions; namespace BlazingBudget.Domain.Aggregates.Budgets diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/ExpenseId.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/ExpenseId.cs deleted file mode 100644 index 67d88c7..0000000 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/ExpenseId.cs +++ /dev/null @@ -1,8 +0,0 @@ -using StronglyTypedIds; - -namespace BlazingBudget.Domain.Aggregates.Budgets; -/// -/// Income unique identifier. -/// -[StronglyTypedId] -public readonly partial struct ExpenseId { } diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/ExpensePaymentCreated.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/ExpensePaymentCreated.cs index 3125187..94997e1 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/ExpensePaymentCreated.cs +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/ExpensePaymentCreated.cs @@ -1,9 +1,6 @@ -using BlazingBudget.Domain.ValueObjects; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using BlazingBudget.Domain.Aggregates.Expenses; +using BlazingBudget.Domain.Aggregates.Payments; +using BlazingBudget.Domain.ValueObjects; namespace BlazingBudget.Domain.Aggregates.Budgets { diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Payment.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Payment.cs deleted file mode 100644 index 6fd2336..0000000 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Payment.cs +++ /dev/null @@ -1,41 +0,0 @@ -using BlazingBudget.Domain.ValueObjects; - -namespace BlazingBudget.Domain.Aggregates.Budgets -{ - /// - /// {BudgetPlan}Payment is an entity because the amount of a payment over time affects long term finances. - /// - public class Payment : AuditableEntity - { - private Payment() { } - - [JsonConstructor] - private Payment(PaymentId id, Money amount, string notes) - { - Id = id; - Amount = amount; - Notes = notes; - CreatedOn = DateTime.UtcNow; - ModifiedOn = CreatedOn; - } - - public static Payment Create(Money amount, string notes = "") - { - return new Payment(new PaymentId(Guid.NewGuid()), amount, notes); - } - - public void ChangeAmount(Money amount) - { - Amount = amount; - } - - public void ChangeNotes(string notes) - { - Notes = notes; - } - - public Money Amount { get; private set; } - - public string Notes { get; set; } = string.Empty; - } -} diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/PaymentId.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/PaymentId.cs deleted file mode 100644 index 63a855b..0000000 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/PaymentId.cs +++ /dev/null @@ -1,13 +0,0 @@ -using StronglyTypedIds; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BlazingBudget.Domain.Aggregates.Budgets; -/// -/// Payment unique identifier. -/// -[StronglyTypedId] -public readonly partial struct PaymentId { } diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Debts/Debt.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Debts/Debt.cs index ac66883..d8479ac 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Debts/Debt.cs +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Debts/Debt.cs @@ -1,4 +1,4 @@ -using BlazingBudget.Domain.Aggregates.Budgets; +using BlazingBudget.Domain.Aggregates.Payments; using BlazingBudget.Domain.ValueObjects; using CSharpFunctionalExtensions; diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Debts/DebtPaymentCreatedEvent.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Debts/DebtPaymentCreatedEvent.cs index e3ace25..0409fdf 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Debts/DebtPaymentCreatedEvent.cs +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Debts/DebtPaymentCreatedEvent.cs @@ -1,10 +1,5 @@ -using BlazingBudget.Domain.Aggregates.Budgets; +using BlazingBudget.Domain.Aggregates.Payments; using BlazingBudget.Domain.ValueObjects; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BlazingBudget.Domain.Aggregates.Debts { diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Expenses/.gitkeep b/src/Domain/BlazingBudget.Domain/Aggregates/Expenses/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Expense.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Expenses/Expense.cs similarity index 85% rename from src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Expense.cs rename to src/Domain/BlazingBudget.Domain/Aggregates/Expenses/Expense.cs index f89f63b..0078cbd 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Expense.cs +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Expenses/Expense.cs @@ -1,7 +1,8 @@ -using BlazingBudget.Domain.Abstractions; +using BlazingBudget.Domain.Abstractions; +using BlazingBudget.Domain.Aggregates.Payments; using BlazingBudget.Domain.ValueObjects; -namespace BlazingBudget.Domain.Aggregates.Budgets; +namespace BlazingBudget.Domain.Aggregates.Expenses; public class Expense : AuditableEntity { private Expense() { } diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Expenses/ExpenseId.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Expenses/ExpenseId.cs new file mode 100644 index 0000000..37390e2 --- /dev/null +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Expenses/ExpenseId.cs @@ -0,0 +1,8 @@ +using StronglyTypedIds; + +namespace BlazingBudget.Domain.Aggregates.Expenses; +/// +/// Expense unique identifier. +/// +[StronglyTypedId] +public readonly partial struct ExpenseId { } diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Incomes/.gitkeep b/src/Domain/BlazingBudget.Domain/Aggregates/Incomes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Income.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Incomes/Income.cs similarity index 89% rename from src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Income.cs rename to src/Domain/BlazingBudget.Domain/Aggregates/Incomes/Income.cs index 5c8846c..b150700 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/Income.cs +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Incomes/Income.cs @@ -1,7 +1,7 @@ -using BlazingBudget.Domain.Abstractions; +using BlazingBudget.Domain.Abstractions; using BlazingBudget.Domain.ValueObjects; -namespace BlazingBudget.Domain.Aggregates.Budgets; +namespace BlazingBudget.Domain.Aggregates.Incomes; /// /// {BudgetPlan}Income is an entity because the amount of a payment over time affects long term finances. /// diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/IncomeId.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Incomes/IncomeId.cs similarity index 61% rename from src/Domain/BlazingBudget.Domain/Aggregates/Budgets/IncomeId.cs rename to src/Domain/BlazingBudget.Domain/Aggregates/Incomes/IncomeId.cs index 856aafc..dfe0424 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Budgets/IncomeId.cs +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Incomes/IncomeId.cs @@ -1,6 +1,6 @@ -using StronglyTypedIds; +using StronglyTypedIds; -namespace BlazingBudget.Domain.Aggregates.Budgets; +namespace BlazingBudget.Domain.Aggregates.Incomes; /// /// Income unique identifier. /// diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Payments/.gitkeep b/src/Domain/BlazingBudget.Domain/Aggregates/Payments/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Payments/Payment.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Payments/Payment.cs new file mode 100644 index 0000000..1a4d44b --- /dev/null +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Payments/Payment.cs @@ -0,0 +1,39 @@ +using BlazingBudget.Domain.ValueObjects; + +namespace BlazingBudget.Domain.Aggregates.Payments; +/// +/// {BudgetPlan}Payment is an entity because the amount of a payment over time affects long term finances. +/// +public class Payment : AuditableEntity +{ + private Payment() { } + + [JsonConstructor] + private Payment(PaymentId id, Money amount, string notes) + { + Id = id; + Amount = amount; + Notes = notes; + CreatedOn = DateTime.UtcNow; + ModifiedOn = CreatedOn; + } + + public static Payment Create(Money amount, string notes = "") + { + return new Payment(new PaymentId(Guid.NewGuid()), amount, notes); + } + + public void ChangeAmount(Money amount) + { + Amount = amount; + } + + public void ChangeNotes(string notes) + { + Notes = notes; + } + + public Money Amount { get; private set; } + + public string Notes { get; set; } = string.Empty; +} diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Payments/PaymentId.cs b/src/Domain/BlazingBudget.Domain/Aggregates/Payments/PaymentId.cs new file mode 100644 index 0000000..ba4e6ab --- /dev/null +++ b/src/Domain/BlazingBudget.Domain/Aggregates/Payments/PaymentId.cs @@ -0,0 +1,8 @@ +using StronglyTypedIds; + +namespace BlazingBudget.Domain.Aggregates.Payments; +/// +/// Payment unique identifier. +/// +[StronglyTypedId] +public readonly partial struct PaymentId { } diff --git a/src/Domain/BlazingBudget.Domain/ValueObjects/Email.cs b/src/Domain/BlazingBudget.Domain/ValueObjects/Email.cs new file mode 100644 index 0000000..b12ad7d --- /dev/null +++ b/src/Domain/BlazingBudget.Domain/ValueObjects/Email.cs @@ -0,0 +1,42 @@ +using CSharpFunctionalExtensions; +using BlazingBudget.Domain.Common; + +namespace BlazingBudget.Domain.ValueObjects; + +public class Email : ValueObject +{ + public string Value { get; private set; } + + public const int MaxLength = 256; + + private Email() { } + + [JsonConstructor] + private Email(string value) + { + Value = value; + } + + public static Result Create(string input) + { + // TODO: How do we reuse these validation rules? + + if (string.IsNullOrWhiteSpace(input)) + return Result.Failure(Messages.Strings.ValueIsRequired); + + string email = input.Trim(); + + if (email.Length > MaxLength) + return Result.Failure(Messages.Strings.ValueIsTooLong); + + if (!Regex.IsMatch(email, @"^(.+)@(.+)$")) + return Result.Failure(Messages.Strings.ValueIsInvalid); + + return Result.Success(new Email(email)); + } + + protected override IEnumerable GetEqualityComponents() + { + yield return Value; + } +} diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Debts/MoneyOwed.cs b/src/Domain/BlazingBudget.Domain/ValueObjects/MoneyOwed.cs similarity index 95% rename from src/Domain/BlazingBudget.Domain/Aggregates/Debts/MoneyOwed.cs rename to src/Domain/BlazingBudget.Domain/ValueObjects/MoneyOwed.cs index 7e88b87..d468f14 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Debts/MoneyOwed.cs +++ b/src/Domain/BlazingBudget.Domain/ValueObjects/MoneyOwed.cs @@ -1,7 +1,7 @@ using CSharpFunctionalExtensions; using BlazingBudget.Domain.ValueObjects; -namespace BlazingBudget.Domain.Aggregates.Debts; +namespace BlazingBudget.Domain.ValueObjects; /// /// Money owed is an amount of money still left to pay. /// diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Debts/MoneyPaid.cs b/src/Domain/BlazingBudget.Domain/ValueObjects/MoneyPaid.cs similarity index 95% rename from src/Domain/BlazingBudget.Domain/Aggregates/Debts/MoneyPaid.cs rename to src/Domain/BlazingBudget.Domain/ValueObjects/MoneyPaid.cs index feda7c3..6cdf1c5 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Debts/MoneyPaid.cs +++ b/src/Domain/BlazingBudget.Domain/ValueObjects/MoneyPaid.cs @@ -1,6 +1,6 @@ using BlazingBudget.Domain.ValueObjects; -namespace BlazingBudget.Domain.Aggregates.Debts; +namespace BlazingBudget.Domain.ValueObjects; /// /// Money paid is an amount of money paid. /// diff --git a/src/Domain/BlazingBudget.Domain/ValueObjects/Password.cs b/src/Domain/BlazingBudget.Domain/ValueObjects/Password.cs new file mode 100644 index 0000000..0291eb9 --- /dev/null +++ b/src/Domain/BlazingBudget.Domain/ValueObjects/Password.cs @@ -0,0 +1,31 @@ +using CSharpFunctionalExtensions; + +namespace BlazingBudget.Domain.ValueObjects; + +public class Password : ValueObject +{ + public string Value { get; private set; } + + private Password() { } + + [JsonConstructor] + private Password(string value) + { + Value = value; + } + + public static Result Create(string value) + { + if (Regex.Match(value, "(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9])(?=.{8,})").Success == false) + { + return Result.Failure("The password does not meet the required strength."); + } + + return Result.Success(new Password(value)); + } + + protected override IEnumerable GetEqualityComponents() + { + yield return Value; + } +} diff --git a/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/PersonName.cs b/src/Domain/BlazingBudget.Domain/ValueObjects/PersonName.cs similarity index 95% rename from src/Domain/BlazingBudget.Domain/Aggregates/Accounts/PersonName.cs rename to src/Domain/BlazingBudget.Domain/ValueObjects/PersonName.cs index 5a24e90..d0d6faf 100644 --- a/src/Domain/BlazingBudget.Domain/Aggregates/Accounts/PersonName.cs +++ b/src/Domain/BlazingBudget.Domain/ValueObjects/PersonName.cs @@ -1,6 +1,6 @@ using CSharpFunctionalExtensions; -namespace BlazingBudget.Domain.Aggregates.Accounts; +namespace BlazingBudget.Domain.ValueObjects; public class PersonName : ValueObject { public string FirstName { get; private set; } diff --git a/src/Infrastructure/BlazingBudget.Infrastructure/Persistence/EntityFramework/BudgetContext.cs b/src/Infrastructure/BlazingBudget.Infrastructure/Persistence/EntityFramework/BudgetContext.cs index d9434d5..8126abe 100644 --- a/src/Infrastructure/BlazingBudget.Infrastructure/Persistence/EntityFramework/BudgetContext.cs +++ b/src/Infrastructure/BlazingBudget.Infrastructure/Persistence/EntityFramework/BudgetContext.cs @@ -1,6 +1,9 @@ using BlazingBudget.Domain.Aggregates.Accounts; using BlazingBudget.Domain.Aggregates.Budgets; using BlazingBudget.Domain.Aggregates.Debts; +using BlazingBudget.Domain.Aggregates.Expenses; +using BlazingBudget.Domain.Aggregates.Incomes; +using BlazingBudget.Domain.Aggregates.Payments; using BlazingBudget.Domain.ValueObjects; using BlazingBudget.Infrastructure.Persistence.ValueConverters; using Microsoft.EntityFrameworkCore; diff --git a/src/Infrastructure/BlazingBudget.Infrastructure/Persistence/ValueConverters/StronglyTypedIdConverters.cs b/src/Infrastructure/BlazingBudget.Infrastructure/Persistence/ValueConverters/StronglyTypedIdConverters.cs index 303d8b9..a7d7f47 100644 --- a/src/Infrastructure/BlazingBudget.Infrastructure/Persistence/ValueConverters/StronglyTypedIdConverters.cs +++ b/src/Infrastructure/BlazingBudget.Infrastructure/Persistence/ValueConverters/StronglyTypedIdConverters.cs @@ -1,6 +1,9 @@ using BlazingBudget.Domain.Aggregates.Accounts; using BlazingBudget.Domain.Aggregates.Budgets; using BlazingBudget.Domain.Aggregates.Debts; +using BlazingBudget.Domain.Aggregates.Expenses; +using BlazingBudget.Domain.Aggregates.Incomes; +using BlazingBudget.Domain.Aggregates.Payments; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace BlazingBudget.Infrastructure.Persistence.ValueConverters; diff --git a/tests/BlazingBudget.Application.Tests/BudgetModelTests.cs b/tests/BlazingBudget.Application.Tests/BudgetModelTests.cs index ff37261..9169ba0 100644 --- a/tests/BlazingBudget.Application.Tests/BudgetModelTests.cs +++ b/tests/BlazingBudget.Application.Tests/BudgetModelTests.cs @@ -2,6 +2,8 @@ using BlazingBudget.Application.Mapping; using BlazingBudget.Domain.Aggregates.Accounts; using BlazingBudget.Domain.Aggregates.Budgets; +using BlazingBudget.Domain.Aggregates.Expenses; +using BlazingBudget.Domain.Aggregates.Incomes; using BlazingBudget.Domain.ValueObjects; using Mapster;