From f6f6232ca6e65b121b728ceafa2f8852901d5f6c Mon Sep 17 00:00:00 2001 From: Anton Skvortsov Date: Thu, 1 May 2025 23:52:38 +1200 Subject: [PATCH 1/4] Refactored. Look at the Readme.md ### Code refactoring improvements to see a summary of changes --- README.md | 79 ++++ RefactorThis.Domain.Tests/App.config | 10 + .../InvoicePaymentProcessorTests.cs | 428 ++++++++---------- .../Mocks/MockInvoiceRepository.cs | 25 + .../RefactorThis.Domain.Tests.csproj | 1 + RefactorThis.Domain.Tests/packages.config | 9 +- .../Exceptions/InvoiceValidationException.cs | 15 + RefactorThis.Domain/InvoiceService.cs | 149 ------ RefactorThis.Domain/Loggers/AppLoggger.cs | 26 ++ RefactorThis.Domain/Loggers/IAppLogger.cs | 11 + RefactorThis.Domain/Models/PaymentResult.cs | 39 ++ .../RefactorThis.Domain.csproj | 24 +- .../Services/IInvoiceService.cs | 10 + .../Services/InvoiceService.cs | 113 +++++ .../Validators/IInvoiceValidator.cs | 10 + .../Validators/InvoiceValidator.cs | 70 +++ RefactorThis.Persistence/Entities/Invoice.cs | 19 + .../{ => Entities}/Payment.cs | 2 +- RefactorThis.Persistence/Invoice.cs | 31 -- RefactorThis.Persistence/InvoiceRepository.cs | 21 - .../RefactorThis.Persistence.csproj | 4 + .../Repositories/IInvoiceRepository.cs | 11 + .../Repositories/InvoiceRepository.cs | 24 + 23 files changed, 685 insertions(+), 446 deletions(-) create mode 100644 RefactorThis.Domain.Tests/App.config create mode 100644 RefactorThis.Domain.Tests/Mocks/MockInvoiceRepository.cs create mode 100644 RefactorThis.Domain/Exceptions/InvoiceValidationException.cs delete mode 100644 RefactorThis.Domain/InvoiceService.cs create mode 100644 RefactorThis.Domain/Loggers/AppLoggger.cs create mode 100644 RefactorThis.Domain/Loggers/IAppLogger.cs create mode 100644 RefactorThis.Domain/Models/PaymentResult.cs create mode 100644 RefactorThis.Domain/Services/IInvoiceService.cs create mode 100644 RefactorThis.Domain/Services/InvoiceService.cs create mode 100644 RefactorThis.Domain/Validators/IInvoiceValidator.cs create mode 100644 RefactorThis.Domain/Validators/InvoiceValidator.cs create mode 100644 RefactorThis.Persistence/Entities/Invoice.cs rename RefactorThis.Persistence/{ => Entities}/Payment.cs (71%) delete mode 100644 RefactorThis.Persistence/Invoice.cs delete mode 100644 RefactorThis.Persistence/InvoiceRepository.cs create mode 100644 RefactorThis.Persistence/Repositories/IInvoiceRepository.cs create mode 100644 RefactorThis.Persistence/Repositories/InvoiceRepository.cs diff --git a/README.md b/README.md index cd01e89..ae4d7ad 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,82 @@ The overall objective is to refactor the code and keep the tests passing. There +### Code refactoring improvements +* Validation Separation: Moved validation logic to dedicated InvoiceValidator +* Process Decomposition: Split payment processing into discrete, focused methods +* Interface Contracts: Established clear interfaces for all dependencies +* Method Simplification: Broke down monolithic ProcessPayment into single-responsibility methods +* Control Flow: Eliminated nested conditionals for linear readability +* Error Handling: Implemented structured exception handling with code safety +* Added logging for issues investigation + +* The string return type in ProcessPayment is problematic as it forces client to parse unstructured messages; +* a strongly-typed response object would provide clearer contracts and better programmability. + +* Client side, before: + +* Returns unstructured string messages +string result = _invoiceService.ProcessPayment(payment); +* Client must parse strings to determine outcome. How to do that efficiently? + +* Client side, after: + +PaymentResult result = _invoiceService.ProcessPayment(payment); + +if (result.IsSuccess) +{ + // Handle successful payment scenarios + switch (result.Code) + { + case ResultCode.FullyPaid: + _receiptService.Generate(invoice, result.AmountPaid); + _auditService.LogPaymentComplete(result.TransactionId); + break; + + case ResultCode.PartiallyPaid: + _paymentTracker.ScheduleFollowUp( + result.RemainingAmount, + DateTime.Now.AddDays(7)); + break; + + // Other cases + } +} +else // !IsSuccess +{ + // Handle failure scenarios + switch (result.Code) + { + case ResultCode.InsufficientFunds: + _retryHandler.QueueRetry(payment); + break; + + case ResultCode.InvalidPaymentMethod: + _userNotifier.RequestPaymentMethodUpdate(); + break; + + // Other cases + + default: + _alertService.TriggerSupportAlert(result); + break; + } +} + +*** Architectural improvements +* Proper layering with domain entities, services, and persistence +* Dependency injection for better testability +* Clear boundaries between components +* Each class has a single responsibility, easier to modify or extend behavior. Clear separation makes it easier to add new features. + +* Suggested Business Object Enhancements (not implemented): + +* Invoice/Payment Data Enrichment +* Currently missing critical transaction details: Payer/recipient identification (names, contact info) +* Banking/payment details +* Transaction references + + + + + diff --git a/RefactorThis.Domain.Tests/App.config b/RefactorThis.Domain.Tests/App.config new file mode 100644 index 0000000..5367091 --- /dev/null +++ b/RefactorThis.Domain.Tests/App.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/RefactorThis.Domain.Tests/InvoicePaymentProcessorTests.cs b/RefactorThis.Domain.Tests/InvoicePaymentProcessorTests.cs index 3a607fd..4d68065 100644 --- a/RefactorThis.Domain.Tests/InvoicePaymentProcessorTests.cs +++ b/RefactorThis.Domain.Tests/InvoicePaymentProcessorTests.cs @@ -1,247 +1,191 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using NUnit.Framework; -using RefactorThis.Persistence; +using RefactorThis.Domain.Loggers; +using RefactorThis.Domain.Models; +using RefactorThis.Domain.Services; +using RefactorThis.Domain.Tests.Mocks; +using RefactorThis.Domain.Validators; +using RefactorThis.Persistence.Entities; +using RefactorThis.Persistence.Repositories; namespace RefactorThis.Domain.Tests { - [TestFixture] - public class InvoicePaymentProcessorTests - { - [Test] - public void ProcessPayment_Should_ThrowException_When_NoInoiceFoundForPaymentReference( ) - { - var repo = new InvoiceRepository( ); - - Invoice invoice = null; - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ); - var failureMessage = ""; - - try - { - var result = paymentProcessor.ProcessPayment( payment ); - } - catch ( InvalidOperationException e ) - { - failureMessage = e.Message; - } - - Assert.AreEqual( "There is no invoice matching this payment", failureMessage ); - } - - [Test] - public void ProcessPayment_Should_ReturnFailureMessage_When_NoPaymentNeeded( ) - { - var repo = new InvoiceRepository( ); - - var invoice = new Invoice( repo ) - { - Amount = 0, - AmountPaid = 0, - Payments = null - }; - - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ); - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "no payment needed", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnFailureMessage_When_InvoiceAlreadyFullyPaid( ) - { - var repo = new InvoiceRepository( ); - - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 10, - Payments = new List - { - new Payment - { - Amount = 10 - } - } - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ); - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "invoice was already fully paid", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnFailureMessage_When_PartialPaymentExistsAndAmountPaidExceedsAmountDue( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 5, - Payments = new List - { - new Payment - { - Amount = 5 - } - } - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 6 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "the payment is greater than the partial amount remaining", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnFailureMessage_When_NoPartialPaymentExistsAndAmountPaidExceedsInvoiceAmount( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 5, - AmountPaid = 0, - Payments = new List( ) - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 6 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "the payment is greater than the invoice amount", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnFullyPaidMessage_When_PartialPaymentExistsAndAmountPaidEqualsAmountDue( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 5, - Payments = new List - { - new Payment - { - Amount = 5 - } - } - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 5 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "final partial payment received, invoice is now fully paid", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnFullyPaidMessage_When_NoPartialPaymentExistsAndAmountPaidEqualsInvoiceAmount( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 0, - Payments = new List( ) { new Payment( ) { Amount = 10 } } - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 10 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "invoice was already fully paid", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnPartiallyPaidMessage_When_PartialPaymentExistsAndAmountPaidIsLessThanAmountDue( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 5, - Payments = new List - { - new Payment - { - Amount = 5 - } - } - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 1 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "another partial payment received, still not fully paid", result ); - } - - [Test] - public void ProcessPayment_Should_ReturnPartiallyPaidMessage_When_NoPartialPaymentExistsAndAmountPaidIsLessThanInvoiceAmount( ) - { - var repo = new InvoiceRepository( ); - var invoice = new Invoice( repo ) - { - Amount = 10, - AmountPaid = 0, - Payments = new List( ) - }; - repo.Add( invoice ); - - var paymentProcessor = new InvoiceService( repo ); - - var payment = new Payment( ) - { - Amount = 1 - }; - - var result = paymentProcessor.ProcessPayment( payment ); - - Assert.AreEqual( "invoice is now partially paid", result ); - } - } + [TestFixture] + public class InvoicePaymentProcessorTests + { + private IInvoiceService _invoiceService; + private IInvoiceRepository _invoiceRepository; + private IInvoiceValidator _invoiceValidator; + private IAppLogger _logger; + + [SetUp] + public void BeforeEachTest() + { + _invoiceRepository = new MockInvoiceRepository(); + _invoiceValidator = new InvoiceValidator(); + _logger = new AppLogger(); + _invoiceService = new InvoiceService(_invoiceRepository, _invoiceValidator, _logger); + } + + [Test] + public void ProcessPayment_Should_ReturnFailure_When_NoInvoiceFoundForPaymentReference() + { + var payment = new Payment(); + var result = _invoiceService.ProcessPayment(payment); + + Assert.IsFalse(result.IsSuccess); + Assert.AreEqual(ResultCode.InvoiceNotFound, result.Code); + Assert.AreEqual("There is no invoice matching this payment", result.Message); + } + + [Test] + public void ProcessPayment_Should_ReturnSuccess_When_NoPaymentNeeded() + { + var invoice = new Invoice { Amount = 0, Payments = null }; + _invoiceRepository.Add(invoice); + + var payment = new Payment(); + var result = _invoiceService.ProcessPayment(payment); + + Assert.IsTrue(result.IsSuccess); + Assert.AreEqual(ResultCode.NoPaymentNeeded, result.Code); + Assert.AreEqual("no payment needed", result.Message); + } + + [Test] + public void ProcessPayment_Should_ReturnSuccess_When_InvoiceAlreadyFullyPaid() + { + var invoice = new Invoice + { + Amount = 10, + Payments = new List { new Payment { Amount = 10 } } + }; + _invoiceRepository.Add(invoice); + + var payment = new Payment(); + + var result = _invoiceService.ProcessPayment(payment); + + Assert.IsTrue(result.IsSuccess); + Assert.AreEqual(ResultCode.AlreadyFullyPaid, result.Code); + Assert.AreEqual("invoice was already fully paid", result.Message); + } + + [Test] + public void ProcessPayment_Should_ReturnFailure_When_PartialPaymentExistsAndAmountPaidExceedsAmountDue() + { + // Arrange + var invoice = new Invoice + { + Amount = 10, + Payments = new List { new Payment { Amount = 5 } } + }; + _invoiceRepository.Add(invoice); + + var payment = new Payment { Amount = 6 }; + + var result = _invoiceService.ProcessPayment(payment); + + Assert.IsFalse(result.IsSuccess); + Assert.AreEqual(ResultCode.PaymentExceedsRemainingAmount, result.Code); + Assert.AreEqual("the payment is greater than the partial amount remaining", result.Message); + } + + [Test] + public void ProcessPayment_Should_ReturnFailure_When_NoPartialPaymentExistsAndAmountPaidExceedsInvoiceAmount() + { + // Arrange + var invoice = new Invoice { Amount = 5, Payments = new List() }; + _invoiceRepository.Add(invoice); + var payment = new Payment { Amount = 6 }; + + // Act + var result = _invoiceService.ProcessPayment(payment); + + // Assert + Assert.IsFalse(result.IsSuccess); + Assert.AreEqual(ResultCode.PaymentExceedsInvoiceAmount, result.Code); + Assert.AreEqual("the payment is greater than the invoice amount", result.Message); + } + + [Test] + public void ProcessPayment_Should_ReturnSuccess_When_PartialPaymentExistsAndAmountPaidEqualsAmountDue() + { + // Arrange + var invoice = new Invoice + { + Amount = 10, + Payments = new List { new Payment { Amount = 5 } } + }; + _invoiceRepository.Add(invoice); + var payment = new Payment { Amount = 5 }; + + // Act + var result = _invoiceService.ProcessPayment(payment); + + // Assert + Assert.IsTrue(result.IsSuccess); + Assert.AreEqual(ResultCode.FinalPaymentComplete, result.Code); + Assert.AreEqual("final partial payment received, invoice is now fully paid", result.Message); + } + + [Test] + public void ProcessPayment_Should_ReturnSuccess_When_PartialPaymentExistsAndAmountPaidIsLessThanAmountDue() + { + // Arrange + var invoice = new Invoice + { + Amount = 10, + Payments = new List { new Payment { Amount = 5 } } + }; + _invoiceRepository.Add(invoice); + var payment = new Payment { Amount = 1 }; + + // Act + var result = _invoiceService.ProcessPayment(payment); + + // Assert + Assert.IsTrue(result.IsSuccess); + Assert.AreEqual(ResultCode.PartialPaymentComplete, result.Code); + Assert.AreEqual("partial payment received, invoice is still not fully paid", result.Message); + } + + [Test] + public void ProcessPayment_Should_ReturnSuccess_When_NoPartialPaymentExistsAndAmountPaidIsLessThanInvoiceAmount() + { + // Arrange + var invoice = new Invoice { Amount = 10, Payments = new List() }; + _invoiceRepository.Add(invoice); + var payment = new Payment { Amount = 1 }; + + // Act + var result = _invoiceService.ProcessPayment(payment); + + // Assert + Assert.IsTrue(result.IsSuccess); + Assert.AreEqual(ResultCode.PartialPaymentComplete, result.Code); + Assert.AreEqual("invoice is now partially paid", result.Message); + } + + [Test] + public void ProcessPayment_Should_CalculateTax_When_CommercialInvoice() + { + // Arrange + var invoice = new Invoice + { + Amount = 10, + Payments = new List(), + Type = InvoiceType.Commercial + }; + _invoiceRepository.Add(invoice); + var payment = new Payment { Amount = 10 }; + + // Act + var result = _invoiceService.ProcessPayment(payment); + + // Assert + Assert.AreEqual(1.4m, invoice.TaxAmount); + Assert.IsTrue(result.IsSuccess); + } + } } \ No newline at end of file diff --git a/RefactorThis.Domain.Tests/Mocks/MockInvoiceRepository.cs b/RefactorThis.Domain.Tests/Mocks/MockInvoiceRepository.cs new file mode 100644 index 0000000..be55f70 --- /dev/null +++ b/RefactorThis.Domain.Tests/Mocks/MockInvoiceRepository.cs @@ -0,0 +1,25 @@ +using RefactorThis.Persistence.Entities; +using RefactorThis.Persistence.Repositories; + +namespace RefactorThis.Domain.Tests.Mocks +{ + public class MockInvoiceRepository : IInvoiceRepository + { + private Invoice _invoice; + + public Invoice GetInvoice(string reference) + { + return _invoice; + } + + public void SaveInvoice(Invoice invoice) + { + // In memory implementation for testing + } + + public void Add(Invoice invoice) + { + _invoice = invoice; + } + } +} \ No newline at end of file diff --git a/RefactorThis.Domain.Tests/RefactorThis.Domain.Tests.csproj b/RefactorThis.Domain.Tests/RefactorThis.Domain.Tests.csproj index f118007..935774c 100644 --- a/RefactorThis.Domain.Tests/RefactorThis.Domain.Tests.csproj +++ b/RefactorThis.Domain.Tests/RefactorThis.Domain.Tests.csproj @@ -43,6 +43,7 @@ + diff --git a/RefactorThis.Domain.Tests/packages.config b/RefactorThis.Domain.Tests/packages.config index c108d44..55738bc 100644 --- a/RefactorThis.Domain.Tests/packages.config +++ b/RefactorThis.Domain.Tests/packages.config @@ -1,4 +1,11 @@  - + + + + + + + + \ No newline at end of file diff --git a/RefactorThis.Domain/Exceptions/InvoiceValidationException.cs b/RefactorThis.Domain/Exceptions/InvoiceValidationException.cs new file mode 100644 index 0000000..d29387e --- /dev/null +++ b/RefactorThis.Domain/Exceptions/InvoiceValidationException.cs @@ -0,0 +1,15 @@ +using System; + +namespace RefactorThis.Domain.Exceptions +{ + public class InvoiceValidationException : Exception + { + public InvoiceValidationException(string message) + : base(message) + {} + + public InvoiceValidationException(string message, Exception innerException) + : base(message, innerException) + {} + } +} \ No newline at end of file diff --git a/RefactorThis.Domain/InvoiceService.cs b/RefactorThis.Domain/InvoiceService.cs deleted file mode 100644 index fbd674c..0000000 --- a/RefactorThis.Domain/InvoiceService.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Linq; -using RefactorThis.Persistence; - -namespace RefactorThis.Domain -{ - public class InvoiceService - { - private readonly InvoiceRepository _invoiceRepository; - - public InvoiceService( InvoiceRepository invoiceRepository ) - { - _invoiceRepository = invoiceRepository; - } - - public string ProcessPayment( Payment payment ) - { - var inv = _invoiceRepository.GetInvoice( payment.Reference ); - - var responseMessage = string.Empty; - - if ( inv == null ) - { - throw new InvalidOperationException( "There is no invoice matching this payment" ); - } - else - { - if ( inv.Amount == 0 ) - { - if ( inv.Payments == null || !inv.Payments.Any( ) ) - { - responseMessage = "no payment needed"; - } - else - { - throw new InvalidOperationException( "The invoice is in an invalid state, it has an amount of 0 and it has payments." ); - } - } - else - { - if ( inv.Payments != null && inv.Payments.Any( ) ) - { - if ( inv.Payments.Sum( x => x.Amount ) != 0 && inv.Amount == inv.Payments.Sum( x => x.Amount ) ) - { - responseMessage = "invoice was already fully paid"; - } - else if ( inv.Payments.Sum( x => x.Amount ) != 0 && payment.Amount > ( inv.Amount - inv.AmountPaid ) ) - { - responseMessage = "the payment is greater than the partial amount remaining"; - } - else - { - if ( ( inv.Amount - inv.AmountPaid ) == payment.Amount ) - { - switch ( inv.Type ) - { - case InvoiceType.Standard: - inv.AmountPaid += payment.Amount; - inv.Payments.Add( payment ); - responseMessage = "final partial payment received, invoice is now fully paid"; - break; - case InvoiceType.Commercial: - inv.AmountPaid += payment.Amount; - inv.TaxAmount += payment.Amount * 0.14m; - inv.Payments.Add( payment ); - responseMessage = "final partial payment received, invoice is now fully paid"; - break; - default: - throw new ArgumentOutOfRangeException( ); - } - - } - else - { - switch ( inv.Type ) - { - case InvoiceType.Standard: - inv.AmountPaid += payment.Amount; - inv.Payments.Add( payment ); - responseMessage = "another partial payment received, still not fully paid"; - break; - case InvoiceType.Commercial: - inv.AmountPaid += payment.Amount; - inv.TaxAmount += payment.Amount * 0.14m; - inv.Payments.Add( payment ); - responseMessage = "another partial payment received, still not fully paid"; - break; - default: - throw new ArgumentOutOfRangeException( ); - } - } - } - } - else - { - if ( payment.Amount > inv.Amount ) - { - responseMessage = "the payment is greater than the invoice amount"; - } - else if ( inv.Amount == payment.Amount ) - { - switch ( inv.Type ) - { - case InvoiceType.Standard: - inv.AmountPaid = payment.Amount; - inv.TaxAmount = payment.Amount * 0.14m; - inv.Payments.Add( payment ); - responseMessage = "invoice is now fully paid"; - break; - case InvoiceType.Commercial: - inv.AmountPaid = payment.Amount; - inv.TaxAmount = payment.Amount * 0.14m; - inv.Payments.Add( payment ); - responseMessage = "invoice is now fully paid"; - break; - default: - throw new ArgumentOutOfRangeException( ); - } - } - else - { - switch ( inv.Type ) - { - case InvoiceType.Standard: - inv.AmountPaid = payment.Amount; - inv.TaxAmount = payment.Amount * 0.14m; - inv.Payments.Add( payment ); - responseMessage = "invoice is now partially paid"; - break; - case InvoiceType.Commercial: - inv.AmountPaid = payment.Amount; - inv.TaxAmount = payment.Amount * 0.14m; - inv.Payments.Add( payment ); - responseMessage = "invoice is now partially paid"; - break; - default: - throw new ArgumentOutOfRangeException( ); - } - } - } - } - } - - inv.Save(); - - return responseMessage; - } - } -} \ No newline at end of file diff --git a/RefactorThis.Domain/Loggers/AppLoggger.cs b/RefactorThis.Domain/Loggers/AppLoggger.cs new file mode 100644 index 0000000..f188546 --- /dev/null +++ b/RefactorThis.Domain/Loggers/AppLoggger.cs @@ -0,0 +1,26 @@ +using System; + +namespace RefactorThis.Domain.Loggers +{ + public class AppLogger : IAppLogger + { + private readonly string _typeName = typeof(T).Name; + + public void LogInformation(string message, params object[] args) + { + Console.WriteLine($"[INFO] [{_typeName}] {string.Format(message, args)}"); + } + + public void LogWarning(string message, params object[] args) + { + Console.WriteLine($"[WARN] [{_typeName}] {string.Format(message, args)}"); + } + + public void LogError(Exception ex, string message, params object[] args) + { + Console.WriteLine($"[ERROR] [{_typeName}] {string.Format(message, args)}"); + Console.WriteLine($"Exception: {ex.GetType().Name} - {ex.Message}"); + Console.WriteLine($"Stack Trace: {ex.StackTrace}"); + } + } +} \ No newline at end of file diff --git a/RefactorThis.Domain/Loggers/IAppLogger.cs b/RefactorThis.Domain/Loggers/IAppLogger.cs new file mode 100644 index 0000000..3c74159 --- /dev/null +++ b/RefactorThis.Domain/Loggers/IAppLogger.cs @@ -0,0 +1,11 @@ +using System; + +namespace RefactorThis.Domain.Loggers +{ + public interface IAppLogger + { + void LogInformation(string message, params object[] args); + void LogWarning(string message, params object[] args); + void LogError(Exception ex, string message, params object[] args); + } +} \ No newline at end of file diff --git a/RefactorThis.Domain/Models/PaymentResult.cs b/RefactorThis.Domain/Models/PaymentResult.cs new file mode 100644 index 0000000..de3199e --- /dev/null +++ b/RefactorThis.Domain/Models/PaymentResult.cs @@ -0,0 +1,39 @@ +namespace RefactorThis.Domain.Models +{ + public class PaymentResult + { + public bool IsSuccess { get; } + public ResultCode? Code { get; } + public string Message { get; } + + public static PaymentResult SuccessResult(ResultCode? code = null, string message = null) + => new PaymentResult(true, code, message); + + public static PaymentResult FailureResult(ResultCode? code = null, string message = null) + => new PaymentResult(false, code, message); + + private PaymentResult(bool isSuccess, ResultCode? code = null, string message = null) + { + IsSuccess = isSuccess; + Code = code ?? ResultCode.Unknown; + Message = string.IsNullOrEmpty(message) ? "No comments" : message; + } + } + + public enum ResultCode + { + Unknown, + // Success + NoPaymentNeeded, + AlreadyFullyPaid, + FinalPaymentComplete, + PartialPaymentComplete, + + // Failure + InvoiceNotFound, + InvalidInvoiceState, + PaymentExceedsRemainingAmount, + PaymentExceedsInvoiceAmount, + ProcessingError + } +} \ No newline at end of file diff --git a/RefactorThis.Domain/RefactorThis.Domain.csproj b/RefactorThis.Domain/RefactorThis.Domain.csproj index 753e893..20c3dc7 100644 --- a/RefactorThis.Domain/RefactorThis.Domain.csproj +++ b/RefactorThis.Domain/RefactorThis.Domain.csproj @@ -32,20 +32,42 @@ 4 + + + ..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll + - + + + + + + + + {33cdc796-ff75-449c-9637-59c2efc46361} RefactorThis.Persistence + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + +