Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: separate payment domain #42

Merged
merged 1 commit into from
Nov 17, 2024
Merged

feat: separate payment domain #42

merged 1 commit into from
Nov 17, 2024

Conversation

italopessoa
Copy link
Member

No description provided.

Copy link

coderabbitai bot commented Nov 17, 2024

Walkthrough

The changes in this pull request primarily involve a significant restructuring of the payment-related namespaces and project references within the solution. The project previously named "Bmb.Payment.Core" has been renamed to "Bmb.Payment.Domain," and various files across the solution have been updated to reflect this change. Additionally, new interfaces and classes have been introduced in the domain layer, while several core contracts have been removed. The modifications also include updates to method signatures and import statements across multiple files to ensure alignment with the new domain structure.

Changes

File Change Summary
Bmb Payment.sln Renamed project from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Orders.Gateway/Bmb.Orders.Gateway.csproj Updated project reference from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Orders.Gateway/Presenter.cs Updated namespace import from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Orders.Gateway/Repository/InMemoryOrdersGateway.cs Updated namespace import from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Orders.Gateway/Repository/OrderReplicaRepository.cs Updated namespace import from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Orders.Gateway/ServiceCollectionsExtensions.cs Updated namespace import from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.Api/Controllers/NotificationsController.cs Updated namespace import for ValueObjects from Bmb.Domain.Core to Bmb.Payment.Domain; updated method signature to include xRequestId parameter.
src/Bmb.Payment.Api/Mappers.cs Updated using directive for PaymentType from Bmb.Domain.Core to Bmb.Payment.Domain.ValueObjects.
src/Bmb.Payment.Application/Bmb.Payment.Application.csproj Updated project reference from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.Application/UseCases/CreatePaymentUseCase.cs Updated return type in method signature from Task<Bmb.Domain.Core.Entities.Payment?> to Task<Domain.Entities.Payment?>.
src/Bmb.Payment.Application/UseCases/ICreatePaymentUseCase.cs Updated method signature to change namespace prefix for PaymentType.
src/Bmb.Payment.Application/UseCases/IUpdatePaymentStatusUseCase.cs Updated method signature to change parameter type from Bmb.Domain.Core.Entities.Payment? to Domain.Entities.Payment?.
src/Bmb.Payment.Application/UseCases/UpdatePaymentStatusUseCase.cs Updated method signature to change parameter type from Bmb.Domain.Core.Entities.Payment? to Domain.Entities.Payment?.
src/Bmb.Payment.Controllers/Contracts/IPaymentService.cs Updated import statements to reflect changes in namespaces.
src/Bmb.Payment.Controllers/Dto/CreateOrderPaymentRequestDto.cs Updated import statement from Bmb.Domain.Core.ValueObjects to Bmb.Payment.Domain.ValueObjects.
src/Bmb.Payment.Controllers/PaymentGatewayFactory.cs Updated import statements from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.Controllers/PaymentService.cs Updated import statements from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.Controllers/Presenter.cs Updated method signature for FromEntityToDto to reflect new namespace.
src/Bmb.Payment.Core/Contracts/IPaymentGateway.cs Deleted interface IPaymentGateway.
src/Bmb.Payment.Core/Contracts/IPaymentGatewayFactoryMethod.cs Deleted interface IPaymentGatewayFactoryMethod.
src/Bmb.Payment.Core/Contracts/IPaymentRepository.cs Deleted interface IPaymentRepository.
src/Bmb.Payment.DI/ServiceCollectionsExtensions.cs Updated namespace import from Bmb.Payment.Core.Contracts to Bmb.Payment.Domain.Contracts.
src/Bmb.Payment.Domain/Bmb.Payment.Domain.csproj Updated package reference for Bmb.Domain.Core from version 0.0.9 to 0.0.14.
src/Bmb.Payment.Domain/Contracts/IPaymentGateway.cs Added new interface IPaymentGateway with methods for payment processing.
src/Bmb.Payment.Domain/Contracts/IPaymentGatewayFactoryMethod.cs Added new interface IPaymentGatewayFactoryMethod.
src/Bmb.Payment.Domain/Contracts/IPaymentRepository.cs Added new interface IPaymentRepository with methods for payment management.
src/Bmb.Payment.Domain/Entities/Payment.cs Added new Payment class with properties and methods related to payment entities.
src/Bmb.Payment.Domain/IOrdersGateway.cs Updated namespace from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.Domain/OrderReplicaDto.cs Updated namespace from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.Domain/ValueObjects/PaymentStatus.cs Added new PaymentStatus enum with various payment states.
src/Bmb.Payment.Domain/ValueObjects/PaymentType.cs Added new PaymentType enum for categorizing payment methods.
src/Bmb.Payment.DynamoDb/Bmb.Payment.DynamoDb.csproj Updated project reference from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.DynamoDb/Presenter.cs Updated method signatures to reflect new namespace for Payment.
src/Bmb.Payment.DynamoDb/Repository/InMemoryPaymentRepository.cs Updated method signatures and field types to reflect new namespace for Payment.
src/Bmb.Payment.DynamoDb/Repository/PaymentRepository.cs Updated method signatures to reflect new namespace for Payment.
src/Bmb.Payment.DynamoDb/ServiceExtensions.cs Updated namespace import from Bmb.Payment.Core.Contracts to Bmb.Payment.Domain.Contracts.
src/Bmb.Payment.FakePayment.Gateway/Bmb.Payment.FakePayment.Gateway.csproj Updated project reference from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.FakePayment.Gateway/FakePaymentGatewayService.cs Updated method signatures and imports to reflect new namespace for Payment.
src/Bmb.Payment.FakePayment.Gateway/ServiceCollectionsExtensions.cs Updated using directives to reflect new namespaces.
src/Bmb.Payment.Masstransit/Bmb.Payment.Masstransit.csproj Updated project reference from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.Masstransit/Dispatcher.cs Added new method PublishIntegrationAsync<T>; updated imports.
src/Bmb.Payment.Masstransit/OrderCreatedPaymentConsumer.cs Updated namespace import from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.Masstransit/Presenter.cs Updated namespace import from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.Masstransit/ServiceCollectionsExtensions.cs Updated namespace import from Bmb.Payment.Core.Contracts to Bmb.Payment.Domain.Contracts.
src/Bmb.Payment.MercadoPago.Gateway/Bmb.Payment.MercadoPago.Gateway.csproj Updated project reference from Bmb.Payment.Core to Bmb.Payment.Domain.
src/Bmb.Payment.MercadoPago.Gateway/MercadoPagoService.cs Updated method signatures and imports to reflect new namespace for Payment.
src/Bmb.Payment.MercadoPago.Gateway/ServiceCollectionsExtensions.cs Updated using directives to reflect new namespaces.
tests/Bmb.Orders.Gateway.Test/Repository/OrderReplicaRepositoryTest.cs Updated namespace import from Bmb.Payment.Core to Bmb.Payment.Domain.
tests/Bmb.Payment.Api.Test/Controllers/NotificationsControllerTest.cs Updated references to Payment entity from Bmb.Domain.Core.Entities to Bmb.Payment.Domain.Entities.
tests/Bmb.Payment.Api.Test/PaymentGatewayFactoryTest.cs Updated using directives to reflect new namespaces.
tests/Bmb.Payment.Application.Test/UseCases/CreatePaymentUseCaseTest.cs Updated using directives and entity references to reflect new namespaces.
tests/Bmb.Payment.Application.Test/UseCases/UpdatePaymentStatusUseCaseTest.cs Updated using directives and entity references to reflect new namespaces.
tests/Bmb.Payment.Controllers.Test/PaymentServiceTests.cs Updated using directives and entity references to reflect new namespaces.
tests/Bmb.Payment.DynamoDb.Test/Repository/PaymentRepositoryTest.cs Updated entity references to reflect new namespaces.
tests/Bmb.Payment.Masstransit.Test/OrderCreatedPaymentConsumerTest.cs Updated namespace import from Bmb.Payment.Core to Bmb.Payment.Domain.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant NotificationsController
    participant PaymentGateway
    participant PaymentRepository

    Client->>NotificationsController: Post payment event
    NotificationsController->>PaymentGateway: CreatePaymentAsync(OrderDto)
    PaymentGateway->>PaymentRepository: SaveAsync(Payment)
    PaymentRepository-->>PaymentGateway: Payment saved
    PaymentGateway-->>NotificationsController: Payment created
    NotificationsController-->>Client: 200 OK
Loading

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🧹 Outside diff range and nitpick comments (59)
src/Bmb.Payment.Application/UseCases/ICreatePaymentUseCase.cs (2)

Line range hint 1-7: LGTM! Clean Architecture principles well applied.

The interface is correctly placed in the Application layer and properly depends on the Domain layer. The namespace restructuring aligns well with DDD principles by establishing clear boundaries for the payment domain.


5-5: Consider strengthening the contract design.

A few suggestions to enhance the interface contract:

  1. Consider renaming Execute to a more domain-specific name like CreatePayment to better reflect the ubiquitous language
  2. Consider making the return type non-nullable (Payment instead of Payment?) and use a Result/Either pattern to handle failure cases explicitly

Example refactor:

- Task<Domain.Entities.Payment?> Execute(Guid orderId, Domain.ValueObjects.PaymentType paymentType);
+ Task<Result<Domain.Entities.Payment>> CreatePayment(Guid orderId, Domain.ValueObjects.PaymentType paymentType);
src/Bmb.Payment.Application/UseCases/IUpdatePaymentStatusUseCase.cs (1)

1-1: LGTM: Clean separation of domain boundaries

The namespace change from Core to Domain properly aligns with DDD principles by establishing clear bounded contexts for the payment domain.

src/Bmb.Payment.Domain/Contracts/IPaymentGatewayFactoryMethod.cs (1)

7-10: Consider renaming interface and improving factory method pattern implementation

  1. The interface name could be more concise (e.g., IPaymentGatewayFactory).
  2. Consider using a more specific return type that encapsulates the payment gateway creation logic.

Here's a suggested improvement:

-public interface IPaymentGatewayFactoryMethod
+public interface IPaymentGatewayFactory
 {
-    IPaymentGateway Create(ValueObjects_PaymentType paymentType);
+    IPaymentGateway Create(PaymentType type);
 }

Consider these architectural improvements:

  1. Follow the Factory Method pattern more closely by defining specific factory methods for each payment type
  2. Consider using a registration mechanism for payment gateways to make the system more extensible
  3. Think about adding validation or error handling for unsupported payment types
src/Bmb.Payment.Domain/Contracts/IPaymentGateway.cs (1)

7-12: Interface design looks good, consider adding documentation

The interface follows the Single Responsibility Principle and has clear, focused methods. However, adding XML documentation would improve clarity, especially for the nullable return type in GetPaymentStatusAsync.

Add documentation:

+/// <summary>
+/// Defines operations for interacting with a payment gateway
+/// </summary>
 public interface IPaymentGateway
 {
+    /// <summary>
+    /// Creates a new payment for the specified order
+    /// </summary>
+    /// <param name="order">The order to create payment for</param>
+    /// <returns>The created payment entity</returns>
     Task<Entities.Payment> CreatePaymentAsync(Order order);

+    /// <summary>
+    /// Retrieves the current status of a payment
+    /// </summary>
+    /// <param name="paymentId">The unique identifier of the payment</param>
+    /// <returns>The payment status if found, null otherwise</returns>
     Task<ValueObjects_PaymentStatus?> GetPaymentStatusAsync(string paymentId);
 }
src/Bmb.Payment.FakePayment.Gateway/ServiceCollectionsExtensions.cs (1)

Line range hint 11-14: Consider making the payment type configuration more flexible

While the current implementation is correct, consider extracting the payment type string into a configuration to make it more maintainable and reusable across the application.

 public static void AddFakePaymentGateway(this IServiceCollection services)
 {
-    services.AddKeyedScoped<IPaymentGateway, FakePaymentGatewayService>($"Payment-{nameof(PaymentType.Test)}");
+    const string PAYMENT_TYPE_KEY = nameof(PaymentType.Test);
+    services.AddKeyedScoped<IPaymentGateway, FakePaymentGatewayService>($"Payment-{PAYMENT_TYPE_KEY}");
 }
src/Bmb.Payment.DynamoDb/ServiceExtensions.cs (1)

Line range hint 11-15: Consider enhancing the DynamoDB configuration.

The configuration parameter is currently unused. Consider implementing the options pattern for DynamoDB settings and AWS client configuration.

Here's a suggested implementation:

 public static void ConfigurePersistenceApp(this IServiceCollection services, IConfiguration configuration)
 {
+    services.Configure<DynamoDbOptions>(configuration.GetSection("DynamoDb"));
+    services.AddAWSService<IAmazonDynamoDB>();
     services.AddScoped<IPaymentRepository, PaymentRepository>();
 }

+public class DynamoDbOptions
+{
+    public string TableName { get; set; }
+    public string Region { get; set; }
+}
src/Bmb.Payment.Api/Mappers.cs (1)

Line range hint 15-15: Consider using a factory method for PaymentType creation

Direct casting to domain value objects can bypass domain invariants and validation rules. Consider using a factory method or constructor that ensures the value object's invariants.

- return new CreateOrderPaymentRequestDto(request.OrderId, (PaymentType)request.PaymentType);
+ return new CreateOrderPaymentRequestDto(request.OrderId, PaymentType.From(request.PaymentType));

This approach allows the domain to enforce its invariants and validation rules when creating value objects.

src/Bmb.Payment.Domain/Contracts/IPaymentRepository.cs (2)

7-16: Add XML documentation and consider return type refinement

The interface follows DDD principles by being focused on a single aggregate root (Payment). However, there are some improvements to consider:

  1. Missing XML documentation for the interface and its methods
  2. The SaveAsync method returns the entire entity, which might expose more data than necessary
  3. The method names could be more explicit about their business operations

Consider these improvements:

+/// <summary>
+/// Defines the contract for persisting and retrieving Payment aggregates
+/// </summary>
 public interface IPaymentRepository
 {
+    /// <summary>
+    /// Persists a new payment or updates an existing one
+    /// </summary>
+    /// <param name="payment">The payment aggregate to save</param>
+    /// <returns>The ID of the saved payment</returns>
-    Task<Entities.Payment> SaveAsync(Entities.Payment payment);
+    Task<PaymentId> SaveAsync(Entities.Payment payment);

+    /// <summary>
+    /// Retrieves a payment by its unique identifier
+    /// </summary>
     Task<Entities.Payment?> GetPaymentAsync(PaymentId paymentId);

+    /// <summary>
+    /// Retrieves a payment by its external reference and type
+    /// </summary>
-    Task<Entities.Payment?> GetPaymentAsync(string externalReference, ValueObjects_PaymentType paymentType);
+    Task<Entities.Payment?> GetPaymentByReferenceAsync(string externalReference, DomainPaymentType paymentType);

+    /// <summary>
+    /// Updates only the status of an existing payment
+    /// </summary>
     Task<bool> UpdatePaymentStatusAsync(Entities.Payment payment);
 }

7-16: Consider adding domain events for payment status changes

Following DDD principles, payment status changes should potentially raise domain events for other bounded contexts to react to.

Consider:

  1. Adding a domain event when payment status changes
  2. Using a richer domain model that includes event raising capabilities
  3. Ensuring the repository implementation handles these events appropriately

Would you like me to provide an example implementation of this pattern?

src/Bmb.Payment.Masstransit/OrderCreatedPaymentConsumer.cs (3)

2-2: Consider introducing an anti-corruption layer for external events

While the namespace change correctly reflects the domain separation, the consumer directly converts external integration events to domain DTOs. Consider introducing an anti-corruption layer to protect the domain boundary and reduce coupling.

Consider:

  1. Creating a separate application service to handle the translation
  2. Moving the ToDto() extension method to an anti-corruption layer
  3. Using domain events instead of integration events within the domain boundary

Line range hint 13-19: Add error handling for gateway operations

The SaveCopyAsync operation lacks error handling. Failed gateway operations could lead to lost messages or unclear error states.

Consider adding proper error handling:

 public async Task Consume(ConsumeContext<OrderCreated> context)
 {
     using var scope = logger.BeginScope("Processing {Message} for {OrderCode} {OrderId}",
         context.Message, context.Message.OrderTrackingCode, context.Message.Id);
     {
+        try
+        {
             await ordersGateway.SaveCopyAsync(context.Message.ToDto());
             logger.LogInformation("Message processed: {Message}", context.Message);
+        }
+        catch (Exception ex)
+        {
+            logger.LogError(ex, "Failed to save order copy for {OrderId}", context.Message.Id);
+            throw;
+        }
     }
 }

Line range hint 15-18: Remove redundant scope block

The extra curly braces create an unnecessary scope block within the using statement.

 using var scope = logger.BeginScope("Processing {Message} for {OrderCode} {OrderId}",
     context.Message, context.Message.OrderTrackingCode, context.Message.Id);
-{
     await ordersGateway.SaveCopyAsync(context.Message.ToDto());
     logger.LogInformation("Message processed: {Message}", context.Message);
-}
src/Bmb.Payment.MercadoPago.Gateway/Bmb.Payment.MercadoPago.Gateway.csproj (1)

Update mercadopago-sdk package to version 2.4.1

The mercadopago-sdk package (currently at 2.3.8) has a newer version 2.4.1 available. For the Microsoft packages, since we couldn't verify the latest versions through the API, and considering they are recent 8.0.x versions from February 2024, they appear to be up-to-date.

  • Update mercadopago-sdk from version 2.3.8 to 2.4.1
🔗 Analysis chain

Line range hint 10-15: Verify package versions for updates and security patches

Some packages have specific version constraints. Let's verify if these are the latest stable versions and check for any security advisories.


Let me try another approach to verify the package versions using a different NuGet API endpoint.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check NuGet packages for latest versions and known vulnerabilities

# Check latest versions for each package
for package in "mercadopago-sdk" "Microsoft.AspNetCore.Mvc.Core" "Microsoft.Extensions.Configuration.Binder" "Microsoft.Extensions.Logging.Abstractions" "Microsoft.Extensions.Options"; do
    echo "Checking $package..."
    curl -s "https://api.nuget.org/v3/registration5-semver1/$package/index.json" | jq -r '.items[0].upper'
done

# Check for known vulnerabilities using GitHub Advisory Database
gh api graphql -f query='
{
  securityVulnerabilities(first: 10, ecosystem: NUGET, package: "mercadopago-sdk") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 2627


Script:

#!/bin/bash
# Description: Check NuGet packages using catalog API

# Function to check package version
check_package() {
    local package=$1
    echo "Checking $package..."
    curl -s "https://api.nuget.org/v3-flatcontainer/$package/index.json" | jq -r '.versions[-1]'
}

# Check each package
check_package "mercadopago-sdk"
check_package "Microsoft.AspNetCore.Mvc.Core"
check_package "Microsoft.Extensions.Configuration.Binder"
check_package "Microsoft.Extensions.Logging.Abstractions"
check_package "Microsoft.Extensions.Options"

# Also check if there's a newer SDK version from MercadoPago
curl -s "https://api.nuget.org/v3-flatcontainer/mercadopago.net/index.json" | jq -r '.versions[-1]'

Length of output: 1930

src/Bmb.Payment.Controllers/PaymentGatewayFactory.cs (4)

1-5: Consider relocating PaymentGatewayFactory to the infrastructure layer

The factory is currently in the Controllers namespace but handles infrastructure concerns (service resolution). According to Clean Architecture principles, this should be moved to the infrastructure layer since it deals with concrete implementations and framework dependencies.

Consider moving this class to a more appropriate namespace like Bmb.Payment.Infrastructure.Factories.


Line range hint 8-8: Consider abstracting service resolution from the factory

Direct dependency on IServiceProvider couples the factory to the DI container. This violates the Dependency Inversion Principle.

Consider introducing an abstraction:

- public sealed class PaymentGatewayFactory(IServiceProvider provider) : IPaymentGatewayFactoryMethod
+ public interface IPaymentGatewayResolver
+ {
+     IPaymentGateway ResolveGateway(string gatewayKey);
+ }
+
+ public sealed class PaymentGatewayFactory(IPaymentGatewayResolver resolver) : IPaymentGatewayFactoryMethod

Line range hint 11-17: Replace string-based service resolution with strongly-typed constants

Using string interpolation for service keys is fragile and error-prone. It makes the codebase harder to maintain and refactor.

Consider introducing constants:

+ private static class PaymentGatewayKeys
+ {
+     public const string MercadoPago = "Payment-MercadoPago";
+     public const string Test = "Payment-Test";
+ }

  var gateway = paymentType switch
  {
-     PaymentType.MercadoPago => provider.GetKeyedService<IPaymentGateway>($"Payment-{nameof(PaymentType.MercadoPago)}"),
-     PaymentType.Test => provider.GetKeyedService<IPaymentGateway>($"Payment-{nameof(PaymentType.Test)}"),
+     PaymentType.MercadoPago => resolver.ResolveGateway(PaymentGatewayKeys.MercadoPago),
+     PaymentType.Test => resolver.ResolveGateway(PaymentGatewayKeys.Test),
      _ => null
  };

Error handling is inconsistent across payment gateway implementations

  • MercadoPagoService catches exceptions and returns null, which differs from the PaymentGatewayFactory throwing a DomainException
  • FakePaymentGatewayService has no error handling at all
  • This inconsistency could lead to unpredictable behavior and makes error handling harder at the consumer level

Recommendations:

  • Standardize error handling across all payment gateway implementations
  • Consider throwing domain-specific exceptions instead of returning null
  • Document the expected error handling behavior in the IPaymentGateway interface
🔗 Analysis chain

Line range hint 19-19: Verify error handling across payment gateway implementations

The domain exception is appropriate here, but we should ensure consistent error handling across all payment gateway implementations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all payment gateway implementations to verify error handling
ast-grep --pattern 'class $_ : IPaymentGateway {
  $$$
}'

Length of output: 59


Script:

#!/bin/bash
# Search for files containing IPaymentGateway interface or implementation
rg -l "IPaymentGateway"

# Then search for error handling patterns in those files
rg "throw new" -A 2 -B 2

Length of output: 2866


Script:

#!/bin/bash
# Let's examine the payment gateway implementations more closely
rg -A 10 "class.*PaymentGateway.*implements.*IPaymentGateway" || rg -A 10 "class.*PaymentGateway.*:.*IPaymentGateway"

# Also check the interface definition
cat src/Bmb.Payment.Domain/Contracts/IPaymentGateway.cs

Length of output: 2785


Script:

#!/bin/bash
# Let's check the MercadoPago implementation as well
cat src/Bmb.Payment.MercadoPago.Gateway/MercadoPagoService.cs

# And look for any try-catch blocks in payment-related files
rg -A 5 "try\s*{" src/Bmb.Payment.*

Length of output: 6426

src/Bmb.Payment.FakePayment.Gateway/FakePaymentGatewayService.cs (1)

12-12: Consider implementing IPaymentFactory

While the implementation is correct, creating domain entities directly in the gateway layer might violate DDD principles. Consider introducing a factory interface in the domain layer for creating Payment entities.

Example refactor:

+ private readonly IPaymentFactory _paymentFactory;
+ 
+ public FakePaymentGatewayService(IPaymentFactory paymentFactory)
+ {
+     _paymentFactory = paymentFactory;
+ }

  public Task<Domain.Entities.Payment> CreatePaymentAsync(OrderDto order)
  {
      var id = Guid.NewGuid();
-     return Task.FromResult(new Domain.Entities.Payment
-     {
-         Id = new PaymentId(id),
-         OrderId = order.Id,
-         PaymentType = PaymentType.Test,
-         Amount = order.Total,
-         Created = DateTime.Now,
-         Status = PaymentStatus.Pending,
-         QrCode = "https://fake.qrcode.com",
-         ExternalReference = id.ToString()
-     });
+     return Task.FromResult(_paymentFactory.Create(
+         id,
+         order.Id,
+         PaymentType.Test,
+         order.Total,
+         "https://fake.qrcode.com"
+     ));

Also applies to: 15-15

src/Bmb.Payment.MercadoPago.Gateway/ServiceCollectionsExtensions.cs (1)

Line range hint 1-33: LGTM! Clean architecture and DDD principles are well applied

The service registration follows clean architecture principles by:

  • Keeping infrastructure concerns (MercadoPago gateway) separate from domain logic
  • Using dependency injection for loose coupling
  • Properly using keyed services for payment gateway registration
src/Bmb.Orders.Gateway/Repository/OrderReplicaRepository.cs (5)

5-5: Consider restructuring to better reflect bounded context boundaries

While the namespace change from Core to Domain aligns with DDD principles, the current structure raises some architectural concerns:

  1. The class name OrderReplicaRepository doesn't clearly indicate its relationship with the Payment domain
  2. The location in Orders.Gateway might blur the boundaries between the Orders and Payment bounded contexts

Consider renaming to PaymentOrderReplicaRepository to better express its role in the Payment context.


Line range hint 13-13: Move table name to configuration

The hardcoded table name should be moved to configuration for better maintainability and environment-specific settings.

-    private const string OrdersReplicaTable = "Payment-OrdersReplica";
+    private readonly string _ordersReplicaTable;
+
+    public OrderReplicaRepository(IAmazonDynamoDB database, IConfiguration configuration)
+    {
+        _ordersReplicaTable = configuration.GetValue<string>("DynamoDB:OrdersReplicaTable")
+            ?? throw new ArgumentNullException("DynamoDB:OrdersReplicaTable");
+    }

Line range hint 29-33: Enhance error handling in GetCopyAsync

The current error handling loses valuable context. Consider:

  1. Including the orderId in the error message
  2. Preserving the original exception details
-           throw new DomainException("Error getting order from replica", e);
+           throw new DomainException(
+               $"Failed to retrieve order replica for orderId: {orderId}",
+               new Dictionary<string, object> { { "OrderId", orderId } },
+               e);

Line range hint 35-38: Add error handling to SaveCopyAsync

The method lacks error handling, which could lead to unhandled exceptions in production.

     public Task SaveCopyAsync(OrderDto order, CancellationToken cancellationToken = default)
     {
-        return database.PutItemAsync(OrdersReplicaTable, order.ToDynamoDb(), cancellationToken);
+        try
+        {
+            return database.PutItemAsync(_ordersReplicaTable, order.ToDynamoDb(), cancellationToken);
+        }
+        catch (AmazonDynamoDBException e)
+        {
+            throw new DomainException(
+                $"Failed to save order replica for orderId: {order.Id}",
+                new Dictionary<string, object> { { "OrderId", order.Id } },
+                e);
+        }
     }

Line range hint 9-11: Consider abstracting infrastructure dependencies

To better align with Clean Architecture principles, consider introducing an abstraction layer for DynamoDB operations. This would:

  1. Make the code more testable
  2. Reduce coupling to AWS infrastructure
  3. Make it easier to change the storage implementation
-internal class OrderReplicaRepository(IAmazonDynamoDB database) : IOrdersGateway
+internal class OrderReplicaRepository(IDocumentStore documentStore) : IOrdersGateway
src/Bmb.Payment.Application/UseCases/UpdatePaymentStatusUseCase.cs (1)

Line range hint 19-38: Consider enhancing domain model integrity and error handling

The current implementation has several areas for improvement:

  1. Accepting nullable Payment might indicate a missing domain invariant
  2. Boolean return type doesn't effectively communicate domain outcomes
  3. Method mixes status update and event publishing responsibilities

Consider these improvements:

- public async Task<bool> Execute(Domain.Entities.Payment? payment, PaymentStatus status)
+ public async Task<PaymentUpdateResult> Execute(Domain.Entities.Payment payment, PaymentStatus status)
{
-    if (payment is not null && payment.Status
+    if (payment.Status
         is not PaymentStatus.Approved
         and not PaymentStatus.Cancelled
         and not PaymentStatus.Rejected)
     {
-        payment.Status = status;
-        payment.Updated = DateTime.UtcNow;
+        try 
+        {
+            payment.UpdateStatus(status); // Move status validation to domain entity
+            var updated = await _paymentRepository.UpdatePaymentStatusAsync(payment);
+            if (updated && payment.IsApproved())
+            {
+                await _dispatcher.PublishAsync(new OrderPaymentConfirmed(payment.Id, payment.OrderId));
+            }
+            return PaymentUpdateResult.Success();
+        }
+        catch (InvalidPaymentStateException ex)
+        {
+            return PaymentUpdateResult.Failed(ex.Message);
+        }
     }
-    return false;
+    return PaymentUpdateResult.Failed("Payment status cannot be updated");
}

This refactoring:

  1. Enforces domain invariants by not accepting null
  2. Uses a result object to better express domain outcomes
  3. Moves status update logic to the domain entity

Consider extracting event publishing to a separate domain service to better separate concerns.

src/Bmb.Payment.DynamoDb/Repository/InMemoryPaymentRepository.cs (1)

Line range hint 31-40: Improve update operation with proper validation

The update method needs similar improvements:

  1. Missing parameter validation
  2. Missing cancellation token support
  3. Consider using optimistic concurrency

Apply this diff:

-    public Task<bool> UpdatePaymentStatusAsync(Domain.Entities.Payment payment)
+    public Task<bool> UpdatePaymentStatusAsync(Domain.Entities.Payment payment, CancellationToken cancellationToken = default)
     {
+        ArgumentNullException.ThrowIfNull(payment);
         var index = _payments.FindIndex(p => p.Id == payment.Id);
         if (index < 0)
         {
             return Task.FromResult(false);
         }

+        // Consider adding version check here for optimistic concurrency
         _payments[index] = payment;
         return Task.FromResult(true);
     }
src/Bmb.Payment.Domain/Entities/Payment.cs (2)

41-41: Improve method naming and consistency

The method could be more expressive about the business rule it represents.

Consider this improvement:

-    public bool IsApproved() => Status == PaymentStatus.Approved;
+    public bool HasBeenApproved() => Status == PaymentStatus.Approved;

11-42: Consider enhancing domain model with events and invariants

From a Clean Architecture and DDD perspective, the Payment entity could benefit from additional patterns.

Consider these architectural improvements:

  1. Add domain events for significant state changes (e.g., PaymentApproved, PaymentRejected)
  2. Implement invariant checks using a Guard class
  3. Consider using Value Objects for Amount and ExternalReference
  4. Add a factory method for common creation scenarios

Example domain event:

public class PaymentApprovedEvent : DomainEvent
{
    public PaymentId PaymentId { get; }
    public decimal Amount { get; }
    
    public PaymentApprovedEvent(PaymentId paymentId, decimal amount)
    {
        PaymentId = paymentId;
        Amount = amount;
    }
}
src/Bmb.Payment.Application/UseCases/CreatePaymentUseCase.cs (2)

26-26: Consider enhancing error handling with domain-specific result type

While the current implementation is solid, consider returning a domain-specific result type instead of nullable Payment to better encapsulate business outcomes and error states.

Example implementation:

- public async Task<Domain.Entities.Payment?> Execute(Guid orderId, PaymentType paymentType)
+ public async Task<PaymentResult> Execute(Guid orderId, PaymentType paymentType)

Where PaymentResult could be:

public class PaymentResult
{
    public bool Success { get; }
    public Domain.Entities.Payment? Payment { get; }
    public string? ErrorMessage { get; }
    
    private PaymentResult(bool success, Domain.Entities.Payment? payment = null, string? errorMessage = null)
    {
        Success = success;
        Payment = payment;
        ErrorMessage = errorMessage;
    }
    
    public static PaymentResult Successful(Domain.Entities.Payment payment) => 
        new(true, payment);
    
    public static PaymentResult Failed(string error) => 
        new(false, errorMessage: error);
}

Line range hint 13-24: Add XML documentation for public members

The class follows clean architecture and SOLID principles well, but would benefit from XML documentation for better maintainability.

Add documentation like this:

+ /// <summary>
+ /// Use case for creating a new payment for an order
+ /// </summary>
public class CreatePaymentUseCase : ICreatePaymentUseCase
{
    private readonly IPaymentGatewayFactoryMethod _paymentGatewayFactory;
    private readonly IOrdersGateway  _ordersGateway;
    private readonly IDispatcher _dispatcher;

+    /// <summary>
+    /// Initializes a new instance of the CreatePaymentUseCase
+    /// </summary>
    public CreatePaymentUseCase(...)
tests/Bmb.Payment.Application.Test/UseCases/UpdatePaymentStatusUseCaseTest.cs (1)

28-28: Consider using domain-specific test builders

While AutoFixture is convenient, consider creating a dedicated test builder for Payment entities to ensure domain invariants and business rules are respected in tests.

Example approach:

-var payment = new Fixture().Create<Domain.Entities.Payment>();
+var payment = PaymentBuilder
+    .Create()
+    .WithDefaultValues()
+    .Build();
src/Bmb.Payment.DynamoDb/Presenter.cs (2)

Line range hint 9-23: Add validation for required fields before mapping

While the mapping logic is clean, consider adding validation to ensure required fields are present before creating the DynamoDB item. This would prevent potential runtime errors if the domain entity is in an invalid state.

 internal static Dictionary<string, AttributeValue> ToDynamoDbItem(this Domain.Entities.Payment payment)
 {
+    if (payment == null) throw new ArgumentNullException(nameof(payment));
+    if (payment.Id == null) throw new ArgumentException("Payment ID is required", nameof(payment));
+    if (string.IsNullOrEmpty(payment.ExternalReference)) throw new ArgumentException("External reference is required", nameof(payment));
+
     return new Dictionary<string, AttributeValue>

Line range hint 25-46: Consider factory pattern for domain entity creation

The direct instantiation of the Payment entity might violate DDD principles. Consider introducing a factory method or builder pattern to ensure domain invariants and business rules are properly enforced during entity creation.

-    internal static Domain.Entities.Payment ToDomain(this Dictionary<string, AttributeValue> item)
+    internal static Domain.Entities.Payment ToDomain(this Dictionary<string, AttributeValue> item)
     {
+        ValidateRequiredAttributes(item);
+        var paymentData = ExtractPaymentData(item);
+        return Domain.Entities.Payment.Create(paymentData);
+    }
+
+    private static void ValidateRequiredAttributes(Dictionary<string, AttributeValue> item)
+    {
+        var requiredFields = new[] { "Id", "OrderId", "Status", "PaymentType", "Amount" };
+        foreach (var field in requiredFields)
+        {
+            if (!item.ContainsKey(field))
+                throw new KeyNotFoundException($"Required field '{field}' is missing");
+        }
     }

Additionally, consider extracting the ConvertUpdatedDateTime helper method to a shared utility class for reuse across the codebase.

src/Bmb.Payment.DI/ServiceCollectionsExtensions.cs (3)

Line range hint 20-31: Consider separating infrastructure configurations

The IoCSetup method currently configures multiple infrastructure concerns (persistence, caching, DynamoDB, etc.) in a single place. Consider splitting these into separate extension methods by infrastructure type to improve maintainability and follow Interface Segregation Principle more strictly.

Example refactoring:

- public static void IoCSetup(this IServiceCollection serviceCollection, IConfiguration configuration)
+ public static IServiceCollection AddPaymentInfrastructure(this IServiceCollection services, IConfiguration configuration)
+ {
+     return services
+         .AddPersistence(configuration)
+         .AddCaching(configuration)
+         .AddDataStorage(configuration);
+ }
+
+ public static IServiceCollection AddPaymentApplicationServices(this IServiceCollection services)
+ {
+     return services
+         .AddUseCases()
+         .AddControllers()
+         .AddPaymentGateways();
+ }

Line range hint 57-67: Consider abstracting database-specific health checks

The health check configuration is tightly coupled to DynamoDB. Consider introducing an abstraction layer to make it database-agnostic and align better with Clean Architecture principles.

Example approach:

+ public interface IStorageHealthCheck
+ {
+     string Name { get; }
+     Task<bool> IsHealthy();
+ }

- .AddDynamoDbHealthCheck("Payments")
+ .AddCheck<PaymentStorageHealthCheck>("Payments")

Line range hint 69-72: Avoid direct infrastructure dependencies in configuration

The direct instantiation of AmazonDynamoDBClient couples the configuration to a specific implementation. Consider using options pattern and factory pattern to maintain better separation of concerns.

Example improvement:

+ public class DynamoDbOptions
+ {
+     public string Region { get; set; }
+     public string ServiceUrl { get; set; }
+ }

- services.AddScoped<IAmazonDynamoDB>(_ => new AmazonDynamoDBClient());
+ services.Configure<DynamoDbOptions>(configuration.GetSection("DynamoDb"));
+ services.AddScoped<IAmazonDynamoDB>(sp => {
+     var options = sp.GetRequiredService<IOptions<DynamoDbOptions>>().Value;
+     return new AmazonDynamoDBClient(new AmazonDynamoDBConfig
+     {
+         ServiceURL = options.ServiceUrl,
+         RegionEndpoint = RegionEndpoint.GetBySystemName(options.Region)
+     });
+ });
src/Bmb.Payment.Api/Controllers/NotificationsController.cs (4)

Line range hint 16-24: Consider encapsulating PaymentType enum in domain model

While the controller follows clean architecture principles with proper dependency injection, the direct usage of PaymentType enum in the controller layer might violate DDD principles. Consider encapsulating this domain concept within a proper domain service or factory.

Example approach:

public interface IPaymentTypeResolver
{
    PaymentProvider ResolveFromWebhook(string provider);
}

// Usage in controller
await _paymentService.SyncPaymentStatusWithGatewayAsync(
    @event.Data.Id, 
    _paymentTypeResolver.ResolveFromWebhook("mercadopago")
);

Line range hint 39-41: Validate xRequestId header

The newly added xRequestId parameter lacks validation. Consider adding validation to ensure it's not null or empty for proper request tracing.

 public async Task<IActionResult> Post([FromBody] MercadoPagoWebhookEvent @event,
     [FromHeader(Name = "x-signature")] string xSignature,
-    [FromHeader(Name = "x-request-id")] string xRequestId)
+    [FromHeader(Name = "x-request-id")] string xRequestId)
+{
+    if (string.IsNullOrEmpty(xRequestId))
+    {
+        return BadRequest("x-request-id header is required");
+    }

Line range hint 46-52: Add error handling for payment synchronization

The background operation lacks error handling. Consider adding try-catch blocks and error logging to handle potential failures during payment synchronization.

 Response.OnCompleted(async () =>
 {
     using (_logger.BeginScope("Processing payment {PaymentId}", @event.Data.Id))
     {
+        try {
             await _paymentService.SyncPaymentStatusWithGatewayAsync(@event.Data.Id, PaymentType.MercadoPago);
+        }
+        catch (Exception ex)
+        {
+            _logger.LogError(ex, "Failed to sync payment {PaymentId}", @event.Data.Id);
+        }
     }
 });

Consider using MassTransit for reliable payment status synchronization

Based on the codebase analysis, I noticed that MassTransit is already configured in the project (Bmb.Payment.Masstransit.csproj and related dependencies). Instead of using Response.OnCompleted, you should leverage the existing message queue infrastructure for reliable payment status synchronization:

  • Move the payment synchronization logic to a MassTransit consumer
  • Publish a payment status update event from the webhook handler
  • This ensures reliable processing even if the application restarts
🔗 Analysis chain

Line range hint 44-54: Consider using a background service instead of Response.OnCompleted

Using Response.OnCompleted for payment status synchronization might lead to lost operations if the application shuts down before the task completes. Consider using a proper background service or message queue for reliability.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there's already a background service implementation
ast-grep --pattern 'class $_ : BackgroundService'

Length of output: 51


Script:

#!/bin/bash
# Let's check for any background processing or queue-related implementations
rg -i "background|queue|worker|processor" --type cs -A 5

# Also check for any hosted services
ast-grep --pattern 'class $_ : IHostedService'

# Look for any message broker or queue configurations
rg -i "rabbitmq|servicebus|kafka|message.*queue" --type cs

Length of output: 600


Script:

#!/bin/bash
# Let's check the project files for any message processing infrastructure
fd -e csproj -x cat {}

# Check for any dependency injection configurations related to background processing
rg -i "addhosted|addbackground|addscoped.*service|addsingleton.*service" --type cs -A 2

# Look for any existing payment processing patterns
rg -i "payment.*process|process.*payment" --type cs -A 5

Length of output: 21196

src/Bmb.Payment.DynamoDb/Repository/PaymentRepository.cs (4)

11-13: Consider externalizing DynamoDB configuration

The table name is hardcoded as a constant. This could make it difficult to change the configuration for different environments.

Consider moving the table name to configuration:

-    private const string PaymentTable = "Payments";
+    private readonly string _paymentTable;
+    public PaymentRepository(IAmazonDynamoDB database, IConfiguration configuration)
+    {
+        _paymentTable = configuration.GetValue<string>("DynamoDB:PaymentTable") ?? "Payments";
+    }

Line range hint 14-39: Enhance error handling for database operations

The current implementation doesn't handle potential DynamoDB exceptions, which could lead to unhandled errors in the application layer.

Consider adding proper exception handling:

     public async Task<Domain.Entities.Payment> SaveAsync(Domain.Entities.Payment payment)
     {
+        ArgumentNullException.ThrowIfNull(payment);
+        try
+        {
             await database.PutItemAsync(PaymentTable, payment.ToDynamoDbItem());
             return payment;
+        }
+        catch (AmazonDynamoDBException ex)
+        {
+            throw new RepositoryException("Failed to save payment", ex);
+        }
     }

Line range hint 64-81: Improve update operation robustness

The current implementation has several potential improvements:

  1. Boolean return type provides limited error information
  2. No optimistic concurrency control
  3. Direct dependency on HTTP status codes leaks infrastructure details

Consider implementing these improvements:

-    public async Task<bool> UpdatePaymentStatusAsync(Domain.Entities.Payment payment)
+    public async Task UpdatePaymentStatusAsync(Domain.Entities.Payment payment, string? expectedVersion = null)
     {
         var attributes = payment.ToDynamoDbItem();
         attributes.Remove("Id");
         
+        var conditionExpression = expectedVersion != null ? "Version = :expectedVersion" : null;
         var request = new UpdateItemRequest
         {
             TableName = PaymentTable,
             Key = new Dictionary<string, AttributeValue>
             {
                 { "Id", new AttributeValue { S = payment.Id.Value.ToString() } }
             },
-            AttributeUpdates = attributes.ToDictionary(x => x.Key, x => new AttributeValueUpdate { Action = AttributeAction.PUT, Value = x.Value })
+            AttributeUpdates = attributes.ToDictionary(x => x.Key, x => new AttributeValueUpdate { Action = AttributeAction.PUT, Value = x.Value }),
+            ConditionExpression = conditionExpression
         };
 
-        var response = await database.UpdateItemAsync(request);
-        return response.HttpStatusCode == HttpStatusCode.OK;
+        try
+        {
+            await database.UpdateItemAsync(request);
+        }
+        catch (ConditionalCheckFailedException)
+        {
+            throw new ConcurrencyException($"Payment {payment.Id} was modified by another process");
+        }
     }

Line range hint 1-81: Consider enhancing the repository pattern implementation

While the current implementation follows basic DDD principles, consider these architectural improvements:

  1. Add a Unit of Work pattern for transaction management
  2. Implement repository-specific interfaces for better domain alignment
  3. Add domain event publishing capability

This would better support domain-driven design and maintain a cleaner separation of concerns. Would you like me to provide example implementations for any of these patterns?

tests/Bmb.Payment.Api.Test/PaymentGatewayFactoryTest.cs (3)

3-4: Namespace changes align with DDD principles

The migration from Core to Domain namespace improves domain isolation and better reflects the bounded context of payments. This change supports the separation of concerns and reduces coupling between domains.


Line range hint 17-91: Consider enhancing test coverage and maintainability

While the test structure is solid, consider these improvements:

  1. Add test for null serviceProvider scenario
  2. Extract the "Payment-" prefix to a constant
  3. Consider using test data constants for better maintainability

Example improvement:

 public class PaymentGatewayFactoryTest
 {
+    private const string PAYMENT_PREFIX = "Payment-";
     private readonly IServiceCollection _serviceCollection;
     
     // ... existing code ...
     
     [Fact]
     public void Create_MercadoPago_ReturnsCorrectService()
     {
         // Arrange
         _serviceCollection.AddKeyedScoped<IPaymentGateway, MercadoPagoService>(
-            $"Payment-{nameof(PaymentType.MercadoPago)}");
+            $"{PAYMENT_PREFIX}{nameof(PaymentType.MercadoPago)}");
         
         // ... rest of the test ...
     }
     
+    [Fact]
+    public void Create_NullServiceProvider_ThrowsException()
+    {
+        // Arrange
+        IServiceProvider serviceProvider = null;
+        
+        // Act
+        var action = () => new PaymentGatewayFactory(serviceProvider);
+        
+        // Assert
+        action.Should().ThrowExactly<ArgumentNullException>();
+    }

Line range hint 23-33: Consider decoupling gateway configurations

The test setup reveals tight coupling between payment gateways and their specific configurations. Consider introducing a configuration abstraction to make the gateways more modular and easier to test.

Example approach:

public interface IPaymentGatewayConfiguration
{
    string GetConfigurationValue(string key);
}

public class MercadoPagoConfiguration : IPaymentGatewayConfiguration
{
    private readonly MercadoPagoOptions _options;
    public MercadoPagoConfiguration(MercadoPagoOptions options) => _options = options;
    
    public string GetConfigurationValue(string key) => key switch
    {
        "AccessToken" => _options.AccessToken,
        "NotificationUrl" => _options.NotificationUrl,
        "WebhookSecret" => _options.WebhookSecret,
        _ => throw new ArgumentException($"Unknown configuration key: {key}")
    };
}
tests/Bmb.Payment.DynamoDb.Test/Repository/PaymentRepositoryTest.cs (4)

29-29: LGTM! The entity namespace change aligns with DDD principles.

The movement of the Payment entity from Core to Domain namespace better reflects the domain-centric architecture, improving the separation of concerns.

Consider adding more specific assertions to verify the persistence of domain invariants and business rules, strengthening the domain model validation.


49-49: Enhance test coverage with comprehensive assertions.

While the entity namespace change is correct, the test assertions could be more thorough.

Consider adding assertions for all relevant payment properties:

 using (new AssertionScope())
 {
     result.Should().NotBeNull();
+    result.Id.Should().Be(expectedPayment.Id);
+    result.ExternalReference.Should().Be(expectedPayment.ExternalReference);
+    result.PaymentType.Should().Be(expectedPayment.PaymentType);
     result.Status.Should().Be(expectedPayment.Status);
 }

72-72: Consider adding edge case tests for the external reference lookup.

While the namespace change is correct, the test coverage could be improved.

Consider adding the following test cases:

  1. When multiple payments exist with the same external reference but different payment types
  2. When no payment exists for the given external reference
  3. When the external reference is null or empty

98-98: Consider enhancing the status update operation design.

While the namespace change aligns with DDD, the current implementation could better reflect domain-driven design principles.

Suggestions for improvement:

  1. Instead of returning a boolean, consider returning the updated Payment entity or using a Result/Either type to better handle domain outcomes
  2. Add assertions to verify that the status was actually updated in DynamoDB
  3. Consider implementing status transition rules as part of the domain model to ensure valid state changes
tests/Bmb.Payment.Application.Test/UseCases/CreatePaymentUseCaseTest.cs (2)

6-8: Good domain separation!

The namespace restructuring from Core to Domain better reflects DDD principles by explicitly separating the payment domain. The addition of ValueObjects namespace also shows proper domain modeling.

Consider maintaining this clear boundary separation in future changes to ensure the payment domain remains cohesive and independent.


42-45: Consider enhancing test data generation

The Payment entity construction is correctly updated to use the new domain namespace. However, we could improve the test maintainability.

Consider creating a custom AutoFixture customization for Payment entities:

public class PaymentCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Domain.Entities.Payment>(composer => composer
            .With(p => p.Id, () => new PaymentId(Guid.NewGuid()))
            .With(p => p.Status, PaymentStatus.Pending));
    }
}

Then use it in tests:

var fixture = new Fixture().Customize(new PaymentCustomization());
var expectedPayment = fixture.Create<Domain.Entities.Payment>();

This would:

  1. Centralize domain entity creation logic
  2. Make tests more maintainable
  3. Ensure consistent test data across all tests
tests/Bmb.Payment.Controllers.Test/PaymentServiceTests.cs (1)

44-44: Maintain consistency in entity references

While both lines correctly reference the domain entity, they use different styles:

  • Line 44: Domain.Entities.Payment
  • Line 54: Bmb.Payment.Domain.Entities.Payment

Consider using the same reference style throughout for better maintainability.

-            .ReturnsAsync(new Fixture().Create<Domain.Entities.Payment>());
+            .ReturnsAsync(new Fixture().Create<Bmb.Payment.Domain.Entities.Payment>());

Also applies to: 54-54

src/Bmb.Payment.MercadoPago.Gateway/MercadoPagoService.cs (4)

Line range hint 16-31: Consider extracting mapping logic to improve SRP adherence

While the service correctly acts as an anti-corruption layer between MercadoPago and the domain, it currently handles both integration logic and object mapping. Consider extracting the mapping methods into a dedicated MercadoPagoMapper class to better adhere to the Single Responsibility Principle.

Example structure:

public class MercadoPagoMapper
{
    private const decimal IntegrationPrice = 0.01M;
    
    public PaymentCreateRequest MapToPaymentRequest(OrderDto order) 
    {
        // Move mapping logic here
    }
    
    public DomainPaymentStatus MapToPaymentStatus(string status)
    {
        // Move status mapping here
    }
}

Line range hint 33-65: Improve domain invariant handling and error management

Several improvements could strengthen the domain integrity:

  1. Instead of returning null on errors, consider using a Result pattern or throwing domain-specific exceptions
  2. PaymentId generation should be handled by the domain layer
  3. Consider introducing domain-specific error types

Example implementation:

public async Task<DomainPayment> CreatePaymentAsync(OrderDto order)
{
    try
    {
        var mercadoPagoPayment = await CreateMercadoPagoPaymentAsync(order);
        return _paymentFactory.Create( // Factory from domain
            mercadoPagoPayment.Status,
            mercadoPagoPayment.PointOfInteraction.TransactionData.QrCode,
            mercadoPagoPayment.Id.ToString(),
            order.Id,
            paymentCreateRequest.TransactionAmount.Value
        );
    }
    catch (Exception e)
    {
        _logger.LogError(e, "Error creating payment for Order {OrderId}", order.Id);
        throw new PaymentCreationFailedException(order.Id, e); // Domain exception
    }
}

Line range hint 98-113: Strengthen domain boundaries in status handling

The current implementation exposes infrastructure concerns to the domain layer:

  1. Consider using a non-nullable domain status with an "Unknown" state instead of returning null
  2. Move status mapping to a dedicated value object in the domain layer

Example approach:

public async Task<DomainPaymentStatus> GetPaymentStatusAsync(string paymentId)
{
    var requestOptions = new RequestOptions()
    {
        AccessToken = _mercadoPagoOptions.AccessToken,
    };
    var client = new PaymentClient();
    var payment = await client.GetAsync(long.Parse(paymentId), requestOptions);
    
    return DomainPaymentStatus.FromExternalStatus(payment?.Status ?? "unknown");
}

Line range hint 115-167: Move hard-coded values to configuration

Several values are currently hard-coded that should be configurable:

  1. Email "guest@mercadofiado.com"
  2. Identification type "CPF"
  3. Category "food"
  4. Payment method "pix"
  5. Statement descriptor
  6. Expiration time (15 minutes)

Consider moving these to the MercadoPagoOptions configuration class:

public class MercadoPagoOptions
{
    public string AccessToken { get; set; }
    public string WebhookSecret { get; set; }
    public string GuestEmail { get; set; }
    public string DefaultCategory { get; set; }
    public string PaymentMethod { get; set; }
    public string StatementDescriptor { get; set; }
    public TimeSpan PaymentExpiration { get; set; }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a1fb9e6 and 32b895a.

📒 Files selected for processing (55)
  • Bmb Payment.sln (1 hunks)
  • src/Bmb.Orders.Gateway/Bmb.Orders.Gateway.csproj (1 hunks)
  • src/Bmb.Orders.Gateway/Presenter.cs (1 hunks)
  • src/Bmb.Orders.Gateway/Repository/InMemoryOrdersGateway.cs (1 hunks)
  • src/Bmb.Orders.Gateway/Repository/OrderReplicaRepository.cs (1 hunks)
  • src/Bmb.Orders.Gateway/ServiceCollectionsExtensions.cs (1 hunks)
  • src/Bmb.Payment.Api/Controllers/NotificationsController.cs (1 hunks)
  • src/Bmb.Payment.Api/Mappers.cs (1 hunks)
  • src/Bmb.Payment.Application/Bmb.Payment.Application.csproj (1 hunks)
  • src/Bmb.Payment.Application/UseCases/CreatePaymentUseCase.cs (2 hunks)
  • src/Bmb.Payment.Application/UseCases/ICreatePaymentUseCase.cs (1 hunks)
  • src/Bmb.Payment.Application/UseCases/IUpdatePaymentStatusUseCase.cs (1 hunks)
  • src/Bmb.Payment.Application/UseCases/UpdatePaymentStatusUseCase.cs (2 hunks)
  • src/Bmb.Payment.Controllers/Contracts/IPaymentService.cs (1 hunks)
  • src/Bmb.Payment.Controllers/Dto/CreateOrderPaymentRequestDto.cs (1 hunks)
  • src/Bmb.Payment.Controllers/PaymentGatewayFactory.cs (1 hunks)
  • src/Bmb.Payment.Controllers/PaymentService.cs (1 hunks)
  • src/Bmb.Payment.Controllers/Presenter.cs (1 hunks)
  • src/Bmb.Payment.Core/Contracts/IPaymentGateway.cs (0 hunks)
  • src/Bmb.Payment.Core/Contracts/IPaymentGatewayFactoryMethod.cs (0 hunks)
  • src/Bmb.Payment.Core/Contracts/IPaymentRepository.cs (0 hunks)
  • src/Bmb.Payment.DI/ServiceCollectionsExtensions.cs (1 hunks)
  • src/Bmb.Payment.Domain/Bmb.Payment.Domain.csproj (1 hunks)
  • src/Bmb.Payment.Domain/Contracts/IPaymentGateway.cs (1 hunks)
  • src/Bmb.Payment.Domain/Contracts/IPaymentGatewayFactoryMethod.cs (1 hunks)
  • src/Bmb.Payment.Domain/Contracts/IPaymentRepository.cs (1 hunks)
  • src/Bmb.Payment.Domain/Entities/Payment.cs (1 hunks)
  • src/Bmb.Payment.Domain/IOrdersGateway.cs (1 hunks)
  • src/Bmb.Payment.Domain/OrderReplicaDto.cs (1 hunks)
  • src/Bmb.Payment.Domain/ValueObjects/PaymentStatus.cs (1 hunks)
  • src/Bmb.Payment.Domain/ValueObjects/PaymentType.cs (1 hunks)
  • src/Bmb.Payment.DynamoDb/Bmb.Payment.DynamoDb.csproj (1 hunks)
  • src/Bmb.Payment.DynamoDb/Presenter.cs (2 hunks)
  • src/Bmb.Payment.DynamoDb/Repository/InMemoryPaymentRepository.cs (1 hunks)
  • src/Bmb.Payment.DynamoDb/Repository/PaymentRepository.cs (3 hunks)
  • src/Bmb.Payment.DynamoDb/ServiceExtensions.cs (1 hunks)
  • src/Bmb.Payment.FakePayment.Gateway/Bmb.Payment.FakePayment.Gateway.csproj (1 hunks)
  • src/Bmb.Payment.FakePayment.Gateway/FakePaymentGatewayService.cs (1 hunks)
  • src/Bmb.Payment.FakePayment.Gateway/ServiceCollectionsExtensions.cs (1 hunks)
  • src/Bmb.Payment.Masstransit/Bmb.Payment.Masstransit.csproj (1 hunks)
  • src/Bmb.Payment.Masstransit/Dispatcher.cs (2 hunks)
  • src/Bmb.Payment.Masstransit/OrderCreatedPaymentConsumer.cs (1 hunks)
  • src/Bmb.Payment.Masstransit/Presenter.cs (1 hunks)
  • src/Bmb.Payment.Masstransit/ServiceCollectionsExtensions.cs (1 hunks)
  • src/Bmb.Payment.MercadoPago.Gateway/Bmb.Payment.MercadoPago.Gateway.csproj (1 hunks)
  • src/Bmb.Payment.MercadoPago.Gateway/MercadoPagoService.cs (1 hunks)
  • src/Bmb.Payment.MercadoPago.Gateway/ServiceCollectionsExtensions.cs (1 hunks)
  • tests/Bmb.Orders.Gateway.Test/Repository/OrderReplicaRepositoryTest.cs (1 hunks)
  • tests/Bmb.Payment.Api.Test/Controllers/NotificationsControllerTest.cs (3 hunks)
  • tests/Bmb.Payment.Api.Test/PaymentGatewayFactoryTest.cs (1 hunks)
  • tests/Bmb.Payment.Application.Test/UseCases/CreatePaymentUseCaseTest.cs (2 hunks)
  • tests/Bmb.Payment.Application.Test/UseCases/UpdatePaymentStatusUseCaseTest.cs (2 hunks)
  • tests/Bmb.Payment.Controllers.Test/PaymentServiceTests.cs (4 hunks)
  • tests/Bmb.Payment.DynamoDb.Test/Repository/PaymentRepositoryTest.cs (4 hunks)
  • tests/Bmb.Payment.Masstransit.Test/OrderCreatedPaymentConsumerTest.cs (1 hunks)
💤 Files with no reviewable changes (3)
  • src/Bmb.Payment.Core/Contracts/IPaymentGateway.cs
  • src/Bmb.Payment.Core/Contracts/IPaymentGatewayFactoryMethod.cs
  • src/Bmb.Payment.Core/Contracts/IPaymentRepository.cs
✅ Files skipped from review due to trivial changes (15)
  • Bmb Payment.sln
  • src/Bmb.Orders.Gateway/Presenter.cs
  • src/Bmb.Orders.Gateway/Repository/InMemoryOrdersGateway.cs
  • src/Bmb.Payment.Controllers/Contracts/IPaymentService.cs
  • src/Bmb.Payment.Controllers/Dto/CreateOrderPaymentRequestDto.cs
  • src/Bmb.Payment.Controllers/PaymentService.cs
  • src/Bmb.Payment.Controllers/Presenter.cs
  • src/Bmb.Payment.Domain/Bmb.Payment.Domain.csproj
  • src/Bmb.Payment.Domain/IOrdersGateway.cs
  • src/Bmb.Payment.Domain/OrderReplicaDto.cs
  • src/Bmb.Payment.Domain/ValueObjects/PaymentStatus.cs
  • src/Bmb.Payment.Domain/ValueObjects/PaymentType.cs
  • src/Bmb.Payment.Masstransit/Presenter.cs
  • tests/Bmb.Orders.Gateway.Test/Repository/OrderReplicaRepositoryTest.cs
  • tests/Bmb.Payment.Masstransit.Test/OrderCreatedPaymentConsumerTest.cs
🧰 Additional context used
📓 Path-based instructions (31)
src/Bmb.Orders.Gateway/Repository/OrderReplicaRepository.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Orders.Gateway/ServiceCollectionsExtensions.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Api/Controllers/NotificationsController.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Api/Mappers.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Application/UseCases/CreatePaymentUseCase.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Application/UseCases/ICreatePaymentUseCase.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Application/UseCases/IUpdatePaymentStatusUseCase.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Application/UseCases/UpdatePaymentStatusUseCase.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Controllers/PaymentGatewayFactory.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.DI/ServiceCollectionsExtensions.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Domain/Contracts/IPaymentGateway.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Domain/Contracts/IPaymentGatewayFactoryMethod.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Domain/Contracts/IPaymentRepository.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Domain/Entities/Payment.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.DynamoDb/Presenter.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.DynamoDb/Repository/InMemoryPaymentRepository.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.DynamoDb/Repository/PaymentRepository.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.DynamoDb/ServiceExtensions.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.FakePayment.Gateway/FakePaymentGatewayService.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.FakePayment.Gateway/ServiceCollectionsExtensions.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Masstransit/Dispatcher.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Masstransit/OrderCreatedPaymentConsumer.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.Masstransit/ServiceCollectionsExtensions.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.MercadoPago.Gateway/MercadoPagoService.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

src/Bmb.Payment.MercadoPago.Gateway/ServiceCollectionsExtensions.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

tests/Bmb.Payment.Api.Test/Controllers/NotificationsControllerTest.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

tests/Bmb.Payment.Api.Test/PaymentGatewayFactoryTest.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

tests/Bmb.Payment.Application.Test/UseCases/CreatePaymentUseCaseTest.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

tests/Bmb.Payment.Application.Test/UseCases/UpdatePaymentStatusUseCaseTest.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

tests/Bmb.Payment.Controllers.Test/PaymentServiceTests.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

tests/Bmb.Payment.DynamoDb.Test/Repository/PaymentRepositoryTest.cs (1)

Pattern **/*.cs: Validate all files and structures against Domain Driven Design, SOLID and Clean Architecture principles.

🔇 Additional comments (39)
src/Bmb.Payment.Application/UseCases/IUpdatePaymentStatusUseCase.cs (1)

7-7: Consider adding parameter validation

The nullable payment parameter might lead to runtime issues if not properly validated. Consider either:

  1. Making the parameter non-nullable if a payment is always required
  2. Adding validation logic in the implementing class
src/Bmb.Payment.Domain/Contracts/IPaymentGatewayFactoryMethod.cs (1)

5-10: Verify the integration with existing payment gateways

Let's ensure this new factory interface aligns with existing payment gateway implementations.

✅ Verification successful

Let me gather more information about the implementation details of the factory and gateways to ensure proper integration.


The factory interface is properly integrated with existing payment gateways

The verification shows that:

  • The PaymentGatewayFactory correctly implements IPaymentGatewayFactoryMethod
  • Two payment gateways (MercadoPagoService and FakePaymentGatewayService) implement IPaymentGateway
  • The factory's Create method handles all enum values from PaymentType (Test and MercadoPago)
  • The implementation uses keyed services for dependency injection, ensuring proper gateway resolution
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing payment gateway implementations
echo "Searching for payment gateway implementations..."
rg -t cs "class.*:.*IPaymentGateway" -g "!obj/*"

# Search for existing factory implementations
echo "Searching for existing factory implementations..."
rg -t cs "class.*:.*IPaymentGatewayFactory" -g "!obj/*"

# Search for PaymentType enum usage
echo "Searching for PaymentType enum usage..."
ast-grep --pattern 'enum PaymentType'

Length of output: 1309


Script:

#!/bin/bash
# Check PaymentGatewayFactory implementation
echo "Checking PaymentGatewayFactory implementation..."
rg -t cs -A 10 "class PaymentGatewayFactory" 

# Check MercadoPago and FakePayment gateway implementations
echo "Checking gateway service implementations..."
rg -t cs -A 5 "class MercadoPagoService"
rg -t cs -A 5 "class FakePaymentGatewayService"

# Check PaymentType enum definition
echo "Checking PaymentType enum definition..."
rg -t cs -A 10 "enum PaymentType"

Length of output: 4409

src/Bmb.Orders.Gateway/ServiceCollectionsExtensions.cs (1)

Line range hint 1-15: LGTM! Clean DI registration implementation

The service collection extension follows good practices:

  • Single responsibility for DI configuration
  • Interface-based registration
  • Proper scoping of dependencies
src/Bmb.Payment.FakePayment.Gateway/ServiceCollectionsExtensions.cs (1)

2-3: LGTM! Namespace changes align with DDD principles

The transition from .Core to .Domain better reflects the bounded context of the payment domain and improves the architectural clarity.

src/Bmb.Payment.DynamoDb/ServiceExtensions.cs (1)

2-2: LGTM! Clean Architecture compliance verified.

The namespace change from Core to Domain better reflects the DDD boundaries and maintains proper Clean Architecture layering, with infrastructure (DynamoDb) depending on domain contracts.

src/Bmb.Payment.Api/Mappers.cs (1)

3-3: LGTM: Clean Architecture alignment

The namespace change from Core to Domain.ValueObjects properly reflects the domain-centric organization of the codebase and maintains clean architectural boundaries.

src/Bmb.Payment.Application/Bmb.Payment.Application.csproj (2)

16-16: LGTM! Project reference update aligns with domain-driven design

The change from Core to Domain project reference properly reflects the architectural shift towards domain-driven design, where the domain layer encapsulates the business logic and rules.


16-16: Verify all project references are updated consistently

Let's ensure all projects depending on the former Core project have been updated to reference the new Domain project.

✅ Verification successful

Project references have been updated correctly

The verification shows that there are no remaining references to the old Core project, and all relevant projects are properly referencing the new Domain project. The following projects have the correct Domain project reference:

  • Bmb.Orders.Gateway
  • Bmb.Payment.Application
  • Bmb.Payment.DynamoDb
  • Bmb.Payment.FakePayment.Gateway
  • Bmb.Payment.Masstransit
  • Bmb.Payment.MercadoPago.Gateway
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to the old Core project
# and verify all projects are referencing the new Domain project

# Search for any remaining references to Core project
echo "Checking for remaining references to Core project..."
rg -l "Bmb\.Payment\.Core" --type csproj

# Search for projects referencing the new Domain project
echo "Checking projects referencing the new Domain project..."
rg -l "Bmb\.Payment\.Domain" --type csproj

Length of output: 385


Script:

#!/bin/bash
# Let's try a different approach using find and grep to locate .csproj files
# and check their contents

# Find all .csproj files
find . -name "*.csproj" -type f -exec sh -c '
    echo "=== Checking file: $1 ==="
    grep -H "Bmb\.Payment\.\(Core\|Domain\)" "$1"
' sh {} \;

Length of output: 2508

src/Bmb.Payment.Domain/Contracts/IPaymentRepository.cs (1)

7-16: Verify repository implementation patterns across the domain

The interface follows repository pattern but let's verify consistency across the codebase.

✅ Verification successful

Let me try a different approach to verify the repository patterns.


Repository pattern implementation is consistent across the codebase

The verification shows consistent repository pattern implementation:

  • Interface IPaymentRepository follows standard async repository pattern with CRUD operations
  • Two implementations found: PaymentRepository and InMemoryPaymentRepository, both correctly implementing the interface
  • All repository methods consistently use async/await pattern with Task return types
  • Method naming conventions are consistent with *Async suffix
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent repository patterns across the domain

# Check for other repository interfaces
echo "Checking repository interface patterns..."
ast-grep --pattern 'interface I$_Repository
{
  $$$
}'

# Check for repository implementations
echo "Checking repository implementations..."
ast-grep --pattern 'class $_Repository : I$_Repository
{
  $$$
}'

Length of output: 304


Script:

#!/bin/bash
# Search for all repository interfaces and implementations with ripgrep for better coverage
echo "Searching for repository interfaces..."
rg "interface I\w+Repository" -g "*.cs" -A 5

echo -e "\nSearching for repository implementations..."
rg "class \w+Repository\s*:\s*I\w+Repository" -g "*.cs" -A 5

echo -e "\nSearching for repository method patterns..."
rg "Task<.*?>\s+\w+Async" -g "*.cs" --type cs

Length of output: 6095

src/Bmb.Payment.Masstransit/Bmb.Payment.Masstransit.csproj (1)

16-16: LGTM! Project reference update aligns with domain separation.

The change from Bmb.Payment.Core to Bmb.Payment.Domain correctly reflects the architectural shift towards better domain separation.

Let's verify the consistency of this architectural change across the solution:

✅ Verification successful

Project reference update is consistent across the solution

The verification shows that:

  • No references to the old Bmb.Payment.Core project remain in any .csproj files
  • The new Bmb.Payment.Domain project is properly referenced by all dependent projects
  • The solution file has been updated to include the new project structure

This confirms that the architectural change has been consistently implemented across the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all references to the old Core project have been updated to Domain
# and that there are no lingering references that might cause build issues.

# Check for any remaining references to the old Core project
echo "Checking for any remaining references to Bmb.Payment.Core:"
rg -l "Bmb\.Payment\.Core" --type csproj

# Check for references to the new Domain project to ensure consistency
echo "Checking references to Bmb.Payment.Domain:"
rg -l "Bmb\.Payment\.Domain" --type csproj

# Look for potential broken references in solution files
echo "Checking solution files for project references:"
rg -l "Bmb\.Payment\.(Core|Domain)" --type sln

Length of output: 553


Script:

#!/bin/bash
# Search for project files and solution files without relying on file type
echo "Checking for any remaining references to Bmb.Payment.Core:"
fd -e csproj -x rg -l "Bmb\.Payment\.Core" {}

echo -e "\nChecking references to Bmb.Payment.Domain:"
fd -e csproj -x rg -l "Bmb\.Payment\.Domain" {}

echo -e "\nChecking solution files for project references:"
fd -e sln -x rg -l "Bmb\.Payment\.(Core|Domain)" {}

Length of output: 896

src/Bmb.Orders.Gateway/Bmb.Orders.Gateway.csproj (1)

16-16: LGTM! Project reference update aligns with domain separation.

The change from Bmb.Payment.Core to Bmb.Payment.Domain correctly reflects the architectural decision to separate the payment domain.

Let's verify the project reference:

✅ Verification successful

Project reference update verified and no legacy references found

The verification confirms:

  • The referenced project Bmb.Payment.Domain.csproj exists at the correct location
  • No remaining references to the old Bmb.Payment.Core namespace were found
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the new project reference exists and the solution builds correctly

# Check if the referenced project exists
fd -t f "Bmb.Payment.Domain.csproj$"

# Check for any broken references in the solution
rg -l "Bmb\.Payment\.Core" 

Length of output: 116

src/Bmb.Payment.Masstransit/OrderCreatedPaymentConsumer.cs (1)

2-2: Verify complete namespace migration

Let's ensure all references to the old Core namespace have been updated consistently.

✅ Verification successful

No remaining references to old Core namespace found

Based on the comprehensive search results, there are no remaining references to the old Bmb.Payment.Core namespace in the codebase. All relevant code is properly using either Bmb.Payment.Domain or other current namespaces. The namespace migration appears to be complete and consistent.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to the old namespace
rg "Bmb\.Payment\.Core" --type cs

# Search for potential missed interface implementations
ast-grep --pattern 'using Bmb.Payment.Core;'

Length of output: 82


Script:

#!/bin/bash
# Let's first check all C# files in the repository to understand the namespace structure
fd -e cs -x head -n 5 {}

# Then search for any references to Payment.Domain namespace to verify the migration
rg "Bmb\.Payment\.Domain" --type cs -C 2

# Also check for any potential interface implementations or type references
ast-grep --pattern 'namespace Bmb.Payment.$_'

Length of output: 33835

src/Bmb.Payment.MercadoPago.Gateway/Bmb.Payment.MercadoPago.Gateway.csproj (1)

18-18: LGTM: Project reference updated correctly

The change from Core to Domain project reference aligns well with the PR objective of separating the payment domain.

src/Bmb.Payment.FakePayment.Gateway/Bmb.Payment.FakePayment.Gateway.csproj (1)

25-25: LGTM! Project reference updated correctly.

The change from Core to Domain aligns with the PR objective of separating the payment domain.

Let's verify that all test cases have been updated to use the new domain namespace:

✅ Verification successful

Project reference and namespace updates verified successfully

The verification shows that:

  • No references to the old Bmb.Payment.Core namespace were found in the codebase
  • Both files (FakePaymentGatewayService.cs and ServiceCollectionsExtensions.cs) are properly using the new Bmb.Payment.Domain namespace
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to the old Core namespace in test files
# Expected: No occurrences of the old namespace should be found

# Search for any remaining references to Bmb.Payment.Core in test files
rg -l "Bmb\.Payment\.Core" --type cs $(fd -e cs . "src/Bmb.Payment.FakePayment.Gateway/")

# Search for test files using the new domain namespace to confirm proper updates
rg -l "Bmb\.Payment\.Domain" --type cs $(fd -e cs . "src/Bmb.Payment.FakePayment.Gateway/")

Length of output: 580


Script:

#!/bin/bash
# Let's check the actual content of these files to see if there are any remaining Core references
rg "Bmb\.Payment\.Core" -A 2 -B 2 src/Bmb.Payment.FakePayment.Gateway/FakePaymentGatewayService.cs src/Bmb.Payment.FakePayment.Gateway/ServiceCollectionsExtensions.cs

Length of output: 168

src/Bmb.Payment.DynamoDb/Bmb.Payment.DynamoDb.csproj (2)

24-24: LGTM! Project reference updated correctly

The project reference has been properly updated to reflect the new domain-driven structure, changing from Core to Domain.


24-24: Verify solution-wide impact of the domain separation

Let's verify that all necessary project references have been updated consistently across the solution.

✅ Verification successful

Let me verify the content of the solution file to understand the project structure better.


Domain separation has been properly implemented across the solution

The verification shows that:

  1. No references to the old Bmb.Payment.Core project remain in the solution
  2. The new Bmb.Payment.Domain project is correctly referenced by all dependent projects:
    • Bmb.Payment.Application
    • Bmb.Payment.DynamoDb
    • Bmb.Payment.MercadoPago.Gateway
    • Bmb.Payment.FakePayment.Gateway
    • Bmb.Payment.Masstransit
    • Bmb.Orders.Gateway

The domain separation has been consistently implemented across the entire solution.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to Bmb.Payment.Core across the solution
# Expected: No references to the old Core project should remain

# Search for any remaining references to the old Core project
rg -l "Bmb\.Payment\.Core" --type-add 'proj:*.{csproj,sln}'  --type proj

# Search for references to the new Domain project to ensure consistency
rg -l "Bmb\.Payment\.Domain" --type-add 'proj:*.{csproj,sln}'  --type proj

Length of output: 535


Script:

#!/bin/bash
# Check the content of the solution file to see all project references
cat "Bmb Payment.sln"

# Also check the content of project files that reference Domain
for proj in $(rg -l "Bmb\.Payment\.Domain" --type-add 'proj:*.{csproj,sln}'  --type proj); do
  if [[ "$proj" == *.csproj ]]; then
    echo "=== $proj ==="
    cat "$proj"
  fi
done

Length of output: 18993

src/Bmb.Payment.FakePayment.Gateway/FakePaymentGatewayService.cs (2)

3-5: LGTM: Clean Architecture compliance

The updated imports correctly reference the domain layer, adhering to Clean Architecture principles where dependencies point inward toward the domain.


Line range hint 1-31: Verify test coverage exclusion

The class is marked with [ExcludeFromCodeCoverage]. Since this is a fake implementation for testing, verify if other test classes depend on this implementation.

✅ Verification successful

Let me check the actual usage of this class in the test file to understand if excluding it from code coverage is appropriate.


Code coverage exclusion is appropriate

The [ExcludeFromCodeCoverage] attribute is correctly applied to FakePaymentGatewayService as it's a test double used only in PaymentGatewayFactoryTest.cs for verifying the payment gateway factory's behavior. The class serves as a fake implementation for testing purposes and doesn't need code coverage itself.

  • The class is registered with dependency injection for test payment type
  • It's used to verify that the factory correctly instantiates the fake gateway for test scenarios
  • No other test dependencies were found that would require coverage of this implementation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for references to FakePaymentGatewayService in test files
rg -l "FakePaymentGatewayService" | rg -i "test"

Length of output: 102


Script:

#!/bin/bash
# Check how FakePaymentGatewayService is used in the test file
rg -A 5 "FakePaymentGatewayService" "tests/Bmb.Payment.Api.Test/PaymentGatewayFactoryTest.cs"

Length of output: 543

src/Bmb.Payment.MercadoPago.Gateway/ServiceCollectionsExtensions.cs (1)

3-4: Verify namespace changes across the codebase

The namespace changes from Core to Domain align well with DDD principles by properly separating domain concerns. Let's verify the consistency of these changes across the codebase.

✅ Verification successful

The results show no remaining references to Core.Contracts and consistent usage of Domain.Contracts across the codebase. Let me verify the ValueObjects namespace usage to ensure complete consistency.


Namespace changes are consistent across the codebase

The verification confirms that:

  • All references to Core.Contracts have been completely removed
  • Domain.Contracts is consistently used across the codebase
  • Domain.ValueObjects is properly referenced across multiple components
  • No legacy references to Core.ValueObjects remain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all Core.Contracts references have been updated to Domain.Contracts

# Check for any remaining references to Core.Contracts
echo "Checking for any remaining references to Core.Contracts..."
rg -l "Bmb\.Payment\.Core\.Contracts"

# Check for consistent usage of Domain.Contracts
echo "Verifying consistent usage of Domain.Contracts..."
rg -l "Bmb\.Payment\.Domain\.Contracts"

Length of output: 1518


Script:

#!/bin/bash
# Check for consistent usage of Domain.ValueObjects
echo "Verifying consistent usage of Domain.ValueObjects..."
rg -l "Bmb\.Payment\.Domain\.ValueObjects"

# Check for any remaining references to Core.ValueObjects
echo "Checking for any remaining references to Core.ValueObjects..."
rg -l "Bmb\.Payment\.Core\.ValueObjects"

Length of output: 1913

src/Bmb.Payment.Masstransit/ServiceCollectionsExtensions.cs (2)

3-3: LGTM! Namespace change aligns with DDD principles

The change from Core to Domain namespace better reflects the bounded context of the payment domain and follows DDD principles.


Line range hint 1-39: Consider further domain separation improvements

While the namespace change is a good start, consider these additional improvements to strengthen the domain separation:

  1. The OrderCreatedPaymentConsumer suggests a potential domain boundary crossing. Consider implementing an anti-corruption layer (ACL) to translate between the Orders and Payment domains.
  2. The IDispatcher interface might be better placed in an application layer if it's orchestrating domain operations.

Let's verify the consumer's dependencies:

src/Bmb.Payment.Masstransit/Dispatcher.cs (2)

1-2: LGTM: Clean architecture principles are well applied

The import changes and class structure align well with Domain-Driven Design principles by properly separating concerns and using domain core events.


Line range hint 1-39: Verify event handling patterns across bounded contexts

As part of the payment domain separation, ensure that:

  1. Integration events are used only for cross-bounded context communication
  2. Notification events remain within the payment bounded context
  3. Event schemas are properly versioned for backward compatibility

Let's verify the event usage patterns:

✅ Verification successful

Let me gather more information about the event implementations and their usage patterns.


Let me try one final verification with modified commands to check cross-boundary communication patterns.


Event handling patterns are correctly implemented across bounded contexts

Based on the verification results:

  1. Integration events are properly used for cross-boundary communication (OrderCreated event from Orders to Payment context)
  2. Notifications are correctly scoped within the Payment bounded context
  3. Event schemas follow a consistent pattern with proper interfaces (IBmbIntegrationEvent, IBmbNotification)

Key observations:

  • The OrderCreatedPaymentConsumer correctly handles the integration event from the Orders context
  • The Dispatcher implementation properly separates notification and integration event publishing
  • The event handling infrastructure is well-structured with clear boundaries between domains
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for proper event type usage across bounded contexts
echo "Checking integration event usage..."
rg -t cs "IBmbIntegrationEvent" -C 3

echo "Checking notification event usage..."
rg -t cs "IBmbNotification" -C 3

# Verify event handlers in other bounded contexts
echo "Checking event handlers..."
ast-grep --pattern 'class $_ : IConsumer<$_>'

Length of output: 3980


Script:

#!/bin/bash
# Check for concrete implementations of events
echo "Checking concrete integration events..."
rg -t cs "class.*:.*IBmbIntegrationEvent" -C 2

echo "Checking concrete notification events..."
rg -t cs "class.*:.*IBmbNotification" -C 2

# Check for event consumers/handlers
echo "Checking event consumers..."
rg -t cs "IConsumer<.*>" -C 2

# Check for cross-boundary event references
echo "Checking cross-boundary event usage..."
fd -e cs -E "test" -E "Tests" | xargs rg "using.*Bmb\.((?!Payment).)*\.Events"

Length of output: 1298


Script:

#!/bin/bash
# Check for the OrderCreated event definition and usage
echo "Checking OrderCreated event..."
rg -t cs "OrderCreated" -C 3

# Check for any other event definitions in the codebase
echo "Checking event definitions..."
ast-grep --pattern 'class $_ : $event where $event'

# Check for cross-boundary imports
echo "Checking imports from other domains..."
rg -t cs "^using Bmb\." -C 0

Length of output: 20737

src/Bmb.Payment.Application/UseCases/UpdatePaymentStatusUseCase.cs (2)

1-4: LGTM: Clean separation of concerns in namespace organization

The namespace organization properly separates cross-cutting concerns (events) from domain-specific components, adhering to Clean Architecture principles and DDD bounded contexts.


Line range hint 8-18: LGTM: Well-structured class following SOLID principles

The class demonstrates:

  • Interface segregation (ISP) through focused interface implementation
  • Dependency inversion (DIP) via constructor injection
  • Single responsibility (SRP) handling payment status updates
src/Bmb.Payment.DynamoDb/Repository/InMemoryPaymentRepository.cs (1)

1-4: Consider moving ValueObjects to the payment domain

The import of Domain.Core.ValueObjects might violate domain boundaries. Since we're separating the payment domain, consider moving relevant value objects to Bmb.Payment.Domain.ValueObjects to maintain domain autonomy.

Let's check what value objects are being used from the core domain:

src/Bmb.Payment.Application/UseCases/CreatePaymentUseCase.cs (1)

5-7: LGTM: Clean namespace organization

The namespace changes properly reflect the separation of payment domain from core, following clean architecture principles.

tests/Bmb.Payment.Application.Test/UseCases/UpdatePaymentStatusUseCaseTest.cs (2)

5-6: LGTM: Proper domain separation

The namespace changes correctly reflect the domain-driven design approach by moving from generic Core to a more focused Domain structure.


32-34: LGTM: Well-structured test with proper verifications

The test maintains good practices by:

  • Verifying the repository call with correct domain entity type
  • Using explicit verification of mock calls
  • Properly scoping multiple assertions
src/Bmb.Payment.DynamoDb/Presenter.cs (1)

Line range hint 1-46: Verify Clean Architecture boundaries

The presenter correctly handles the infrastructure concerns of mapping between domain and persistence models. However, to strengthen the architecture:

  1. Consider moving this class to a dedicated "Infrastructure.Persistence" namespace
  2. Implement an interface in the domain layer that this presenter satisfies
  3. Use dependency injection to maintain proper dependency direction
#!/bin/bash
# Check current architecture boundaries
echo "Checking domain dependencies..."
rg --type csproj -l "Bmb.Payment.Domain" | while read -r file; do
  echo "Project referencing domain: $file"
  cat "$file" | grep -A 3 "<ProjectReference.*Bmb.Payment.Domain"
done
src/Bmb.Payment.DI/ServiceCollectionsExtensions.cs (1)

6-6: LGTM! Namespace change aligns with DDD principles

The change from Core to Domain better reflects the bounded context and domain-centric approach of DDD.

src/Bmb.Payment.Api/Controllers/NotificationsController.cs (1)

3-3: LGTM: Proper domain separation

The namespace change correctly reflects the separation of the payment domain, following DDD principles by importing value objects from the domain layer.

src/Bmb.Payment.DynamoDb/Repository/PaymentRepository.cs (1)

4-6: Consider completing the domain separation

There's still a reference to Bmb.Domain.Core.ValueObjects while other namespaces have been migrated to the new domain structure. This might indicate an incomplete separation of concerns.

Consider migrating the remaining Core dependencies to maintain consistency with the domain-centric architecture.

tests/Bmb.Payment.Controllers.Test/PaymentServiceTests.cs (2)

5-5: LGTM: Clean namespace organization aligned with DDD principles

The updated imports properly reflect the domain separation, maintaining a clear boundary between the domain layer and other architectural components.

Also applies to: 10-11


62-62: Verify domain boundary isolation

The entity references have been properly updated to use the domain namespace, but let's verify that no core dependencies leak into the domain layer.

Also applies to: 83-83

✅ Verification successful

Let me gather more information about the domain boundary isolation by checking for any potential leaks in the opposite direction.


Domain boundary isolation is properly maintained

The verification confirms that:

  • Domain project has no external project references
  • Only domain-specific imports are used within domain entities
  • Other layers correctly reference the domain layer without circular dependencies
  • Entity references in tests properly use the domain namespace
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the domain layer remains pure with no dependencies on other layers

# Check for any potential layer violations in project references
find . -name "*.csproj" -exec grep -l "Bmb.Payment.Domain" {} \; | while read proj; do
    echo "Analyzing dependencies in $proj"
    grep -A 10 "<ProjectReference" "$proj" | grep "Bmb.Payment"
done

# Verify that Domain entities are not exposed through non-domain interfaces
ast-grep --pattern 'interface I$_Repository {
  $$$
  Task<Bmb.Payment.Domain.Entities.$_> $_($$$);
  $$$
}'

Length of output: 2658


Script:

#!/bin/bash
# Check if domain project has any dependencies on other layers
grep -A 10 "<ProjectReference" ./src/Bmb.Payment.Domain/Bmb.Payment.Domain.csproj

# Check for any infrastructure or application types being used in domain entities
ast-grep --pattern 'namespace Bmb.Payment.Domain.Entities {
  $$$
  class $_ {
    $$$
  }
}'

# Look for any non-domain namespaces used within domain entities
rg "using Bmb\.Payment\." ./src/Bmb.Payment.Domain/ -A 1

Length of output: 436

tests/Bmb.Payment.Api.Test/Controllers/NotificationsControllerTest.cs (3)

6-6: LGTM: Clean domain separation

The updated import aligns with DDD principles by properly isolating domain concepts into their dedicated namespace.


Line range hint 18-135: LGTM: Well-structured test implementation

The test class demonstrates good testing practices:

  • Clear Arrange-Act-Assert structure
  • Comprehensive scenario coverage
  • Strong assertions and verifications
  • Clean dependency management

45-45: Verify consistent domain entity usage

The Payment entity references have been correctly updated to use the new domain namespace. Let's verify this change is consistent across the codebase.

Also applies to: 120-120

✅ Verification successful

The results show that there are no occurrences of the old namespace Domain.Core.Entities.Payment in the codebase, while the new namespace Domain.Entities.Payment is consistently used across all relevant files. Let me generate the final response:

Payment entity namespace change is consistently applied

The verification confirms that the namespace change for the Payment entity has been properly implemented across the entire codebase. All references now use Domain.Entities.Payment and no legacy references to Domain.Core.Entities.Payment remain.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all Payment entity references use the new domain namespace
# and no old references remain

# Check for any remaining references to the old namespace
rg -l "Domain\.Core\.Entities\.Payment"

# Check for consistent usage of the new namespace
rg -l "Domain\.Entities\.Payment"

Length of output: 1005

src/Bmb.Payment.MercadoPago.Gateway/MercadoPagoService.cs (1)

3-5: LGTM: Namespace changes align with DDD principles

The transition from Core to Domain namespace better reflects the DDD architectural style and provides clearer separation of concerns.

Also applies to: 11-12

Comment on lines +1 to +3
using Bmb.Domain.Core.ValueObjects;
using PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
using ValueObjects_PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify PaymentType imports to avoid confusion

The current import structure with multiple PaymentType references and aliases creates unnecessary complexity. Consider consolidating the PaymentType references to avoid confusion.

-using Bmb.Domain.Core.ValueObjects;
-using PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
-using ValueObjects_PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
+using Bmb.Payment.Domain.ValueObjects;

Committable suggestion skipped: line range outside the PR's diff.


public interface IPaymentGateway
{
Task<Entities.Payment> CreatePaymentAsync(OrderDto order);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Replace OrderDto with domain entity in gateway interface

Using OrderDto in the domain layer violates clean architecture principles. The domain layer should be independent of application concerns and should use domain entities or value objects instead of DTOs.

Consider:

  1. Creating a domain entity/aggregate for Order
  2. Moving DTO transformation to the application layer
-Task<Entities.Payment> CreatePaymentAsync(OrderDto order);
+Task<Entities.Payment> CreatePaymentAsync(Order order);

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +1 to +3
using Bmb.Domain.Core.ValueObjects;
using PaymentStatus = Bmb.Payment.Domain.ValueObjects.PaymentStatus;
using ValueObjects_PaymentStatus = Bmb.Payment.Domain.ValueObjects.PaymentStatus;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify PaymentStatus references to improve clarity

The current setup has multiple PaymentStatus references which could lead to confusion:

  1. One from Domain.Core
  2. Two aliases from Payment.Domain

This might indicate a violation of DDD bounded contexts or unnecessary complexity.

Consider:

  1. Consolidating PaymentStatus to a single location
  2. If different PaymentStatus types are needed, use more meaningful aliases
-using Bmb.Domain.Core.ValueObjects;
-using PaymentStatus = Bmb.Payment.Domain.ValueObjects.PaymentStatus;
-using ValueObjects_PaymentStatus = Bmb.Payment.Domain.ValueObjects.PaymentStatus;
+using Bmb.Payment.Domain.ValueObjects;

Committable suggestion skipped: line range outside the PR's diff.

@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using Bmb.Orders.Gateway.Repository;
using Bmb.Payment.Core;
using Bmb.Payment.Domain;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Refactor cross-domain dependency to respect bounded contexts

The verification reveals a significant coupling between Orders and Payment domains:

  • OrderDto is defined in Payment.Domain but represents an Order entity
  • IOrdersGateway interface is also defined in Payment.Domain but implemented in Orders.Gateway
  • The DTO contains payment-specific fields (PaymentId) mixed with order data

Recommendations:

  • Move OrderDto and IOrdersGateway to a shared kernel or contracts project
  • Split the DTO into OrderDto (in Orders domain) and PaymentOrderDto (in Payment domain)
  • Consider using domain events for cross-boundary communication instead of direct references
🔗 Analysis chain

Review the domain dependency between Orders and Payment

While the namespace change from Core to Domain better reflects DDD principles, the direct reference from Orders.Gateway to Payment.Domain might indicate a coupling between bounded contexts. Consider if this dependency could be inverted or isolated through an anti-corruption layer or domain events.

Let's analyze the usage of Payment.Domain types in the Orders context:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how Payment.Domain types are used in Orders.Gateway
# to determine if the dependency is necessary or could be decoupled

# Search for any usage of types from Payment.Domain
rg -l "Bmb\.Payment\.Domain\." src/Bmb.Orders.Gateway/

# Check for specific Payment.Domain type usages in the codebase
ast-grep --pattern 'using Bmb.Payment.Domain;'

Length of output: 1197


Script:

#!/bin/bash
# Let's check the actual usage of Payment.Domain types in Orders.Gateway files

# Check ServiceCollectionsExtensions.cs
echo "=== ServiceCollectionsExtensions.cs ==="
rg -A 5 "Bmb\.Payment\.Domain" src/Bmb.Orders.Gateway/ServiceCollectionsExtensions.cs

# Check Presenter.cs
echo "=== Presenter.cs ==="
rg -A 5 "Bmb\.Payment\.Domain" src/Bmb.Orders.Gateway/Presenter.cs

# Check OrderReplicaRepository.cs
echo "=== OrderReplicaRepository.cs ==="
rg -A 5 "Bmb\.Payment\.Domain" src/Bmb.Orders.Gateway/Repository/OrderReplicaRepository.cs

# Check InMemoryOrdersGateway.cs
echo "=== InMemoryOrdersGateway.cs ==="
rg -A 5 "Bmb\.Payment\.Domain" src/Bmb.Orders.Gateway/Repository/InMemoryOrdersGateway.cs

# Check project references
echo "=== Project References ==="
cat src/Bmb.Orders.Gateway/Bmb.Orders.Gateway.csproj

Length of output: 2022


Script:

#!/bin/bash
# Let's check what types from Payment.Domain are actually used in the Orders.Gateway

# Check for specific Payment.Domain type usages in OrderReplicaRepository
echo "=== OrderReplicaRepository Implementation ==="
rg -C 10 "class OrderReplicaRepository" src/Bmb.Orders.Gateway/Repository/OrderReplicaRepository.cs

# Check for specific Payment.Domain type usages in InMemoryOrdersGateway
echo "=== InMemoryOrdersGateway Implementation ==="
rg -C 10 "class InMemoryOrdersGateway" src/Bmb.Orders.Gateway/Repository/InMemoryOrdersGateway.cs

# Check for interface definition
echo "=== IOrdersGateway Interface ==="
ast-grep --pattern 'interface IOrdersGateway'

Length of output: 2621


Script:

#!/bin/bash
# Let's check the IOrdersGateway interface definition and OrderDto to understand the coupling

# Check IOrdersGateway interface
echo "=== IOrdersGateway Interface Definition ==="
rg -C 10 "interface IOrdersGateway" src/Bmb.Payment.Domain/IOrdersGateway.cs

# Check OrderDto definition
echo "=== OrderDto Definition ==="
rg -C 10 "class OrderDto" src/Bmb.Payment.Domain/

# Check if OrderDto is used elsewhere in the Payment domain
echo "=== OrderDto Usage in Payment Domain ==="
rg -l "OrderDto" src/Bmb.Payment.Domain/

Length of output: 2094

Comment on lines +1 to +3
using Bmb.Domain.Core.ValueObjects;
using PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
using ValueObjects_PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify import statements to avoid confusion

The current import statements create unnecessary complexity with aliases:

  1. Two imports for PaymentType with different aliases
  2. The alias ValueObjects_PaymentType doesn't follow C# naming conventions

Consider this simplified approach:

using Bmb.Domain.Core.ValueObjects;
-using PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
-using ValueObjects_PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
+using DomainPaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
using Bmb.Domain.Core.ValueObjects;
using PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
using ValueObjects_PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
using Bmb.Domain.Core.ValueObjects;
using DomainPaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;

Comment on lines 8 to +11
[ExcludeFromCodeCoverage]
public class InMemoryPaymentRepository : IPaymentRepository
{
private readonly List<Domain.Core.Entities.Payment> _payments = new();
private readonly List<Domain.Entities.Payment> _payments = new();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Move InMemoryPaymentRepository to test project

The InMemoryPaymentRepository is misplaced in the main DynamoDb project. The codebase shows:

  • A proper PaymentRepository implementation exists in the same directory
  • The DynamoDb project is registered with PaymentRepository as the implementation for IPaymentRepository
  • Test projects already use Moq for mocking IPaymentRepository

Action items:

  • Move InMemoryPaymentRepository to Bmb.Payment.DynamoDb.Test project
  • Remove [ExcludeFromCodeCoverage] attribute as it will be in test project
  • Consider using Moq instead for consistency with other tests
🔗 Analysis chain

Clarify the purpose of excluding code coverage

The [ExcludeFromCodeCoverage] attribute on a repository implementation is concerning. If this is a test double, consider:

  1. Moving it to a test project
  2. Marking it as internal
  3. Adding XML documentation to clarify its testing purpose

Let's check if this is used in tests:


Let me gather more information to understand the repository structure and its usage better.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find references to InMemoryPaymentRepository
rg -l "InMemoryPaymentRepository"

# Check if there's a proper repository implementation
ast-grep --pattern 'class $_Repository : IPaymentRepository'

Length of output: 161


Script:

#!/bin/bash
# Check for test projects and test files
fd -e csproj
rg -l "Test" -g "*.csproj"

# Look for DynamoDb repository implementation
rg -l "DynamoDb.*Repository"

# Check service registration/DI setup
rg -A 5 "IPaymentRepository" -g "*.cs"

Length of output: 8802

Comment on lines +1 to +8
using Bmb.Domain.Core.Base;
using Bmb.Domain.Core.ValueObjects;
using Bmb.Payment.Domain.ValueObjects;
using PaymentStatus = Bmb.Payment.Domain.ValueObjects.PaymentStatus;
using PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
using ValueObjects_PaymentStatus = Bmb.Payment.Domain.ValueObjects.PaymentStatus;
using ValueObjects_PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify using directives by removing redundant aliases

The code currently uses both direct type references and aliases for the same types, which reduces readability. Consider using consistent type references throughout the code.

Apply this diff to clean up the using directives:

 using Bmb.Domain.Core.Base;
 using Bmb.Domain.Core.ValueObjects;
 using Bmb.Payment.Domain.ValueObjects;
-using PaymentStatus = Bmb.Payment.Domain.ValueObjects.PaymentStatus;
-using PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
-using ValueObjects_PaymentStatus = Bmb.Payment.Domain.ValueObjects.PaymentStatus;
-using ValueObjects_PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;

Then update all references in the code to use the direct type names.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
using Bmb.Domain.Core.Base;
using Bmb.Domain.Core.ValueObjects;
using Bmb.Payment.Domain.ValueObjects;
using PaymentStatus = Bmb.Payment.Domain.ValueObjects.PaymentStatus;
using PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
using ValueObjects_PaymentStatus = Bmb.Payment.Domain.ValueObjects.PaymentStatus;
using ValueObjects_PaymentType = Bmb.Payment.Domain.ValueObjects.PaymentType;
using Bmb.Domain.Core.Base;
using Bmb.Domain.Core.ValueObjects;
using Bmb.Payment.Domain.ValueObjects;

Comment on lines +25 to +39
public Payment()
{
Created = DateTime.UtcNow;
}

public Payment(PaymentId id, string qrCode, decimal amount, ValueObjects_PaymentType paymentType = ValueObjects_PaymentType.Test)
: base(id)
{
Id = id;
Status = ValueObjects_PaymentStatus.Pending;
QrCode = qrCode;
Amount = amount;
Created = DateTime.UtcNow;
PaymentType = paymentType;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve constructor implementation and validation

The constructors need improvement in terms of validation and avoiding redundant assignments.

Apply these improvements:

-    public Payment()
+    protected Payment()
     {
         Created = DateTime.UtcNow;
     }
 
     public Payment(PaymentId id, string qrCode, decimal amount, PaymentType paymentType = PaymentType.Test)
         : base(id)
     {
-        Id = id;  // Redundant, already set by base(id)
+        if (string.IsNullOrWhiteSpace(qrCode))
+            throw new DomainException("QrCode is required");
+        if (amount <= 0)
+            throw new DomainException("Amount must be greater than zero");
+
         Status = PaymentStatus.Pending;
         QrCode = qrCode;
         Amount = amount;
         Created = DateTime.UtcNow;
         PaymentType = paymentType;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public Payment()
{
Created = DateTime.UtcNow;
}
public Payment(PaymentId id, string qrCode, decimal amount, ValueObjects_PaymentType paymentType = ValueObjects_PaymentType.Test)
: base(id)
{
Id = id;
Status = ValueObjects_PaymentStatus.Pending;
QrCode = qrCode;
Amount = amount;
Created = DateTime.UtcNow;
PaymentType = paymentType;
}
protected Payment()
{
Created = DateTime.UtcNow;
}
public Payment(PaymentId id, string qrCode, decimal amount, ValueObjects_PaymentType paymentType = ValueObjects_PaymentType.Test)
: base(id)
{
if (string.IsNullOrWhiteSpace(qrCode))
throw new DomainException("QrCode is required");
if (amount <= 0)
throw new DomainException("Amount must be greater than zero");
Status = ValueObjects_PaymentStatus.Pending;
QrCode = qrCode;
Amount = amount;
Created = DateTime.UtcNow;
PaymentType = paymentType;
}

Comment on lines +11 to +23
public class Payment : Entity<PaymentId>, IAggregateRoot
{
public ValueObjects_PaymentType PaymentType { get; set; } = ValueObjects_PaymentType.Test;

public Guid OrderId { get; set; }

public string ExternalReference { get; set; }

public string QrCode { get; set; }

public decimal Amount { get; set; }

public ValueObjects_PaymentStatus Status { get; set; }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Strengthen domain model encapsulation and validation

The current implementation with public setters violates DDD principles by allowing direct state mutation without business rules enforcement.

Consider these improvements:

  1. Make properties private set and provide methods for state changes
  2. Add validation for Amount (should be positive)
  3. Make OrderId non-nullable and validate
  4. Add validation for ExternalReference and QrCode
  5. Implement proper state transitions for Status

Example implementation:

 public class Payment : Entity<PaymentId>, IAggregateRoot
 {
-    public ValueObjects_PaymentType PaymentType { get; set; }
+    public PaymentType PaymentType { get; private set; }
 
-    public Guid OrderId { get; set; }
+    public Guid OrderId { get; private set; }
 
-    public string ExternalReference { get; set; }
+    public string ExternalReference { get; private set; }
 
-    public string QrCode { get; set; }
+    public string QrCode { get; private set; }
 
-    public decimal Amount { get; set; }
+    public decimal Amount { get; private set; }
 
-    public ValueObjects_PaymentStatus Status { get; set; }
+    public PaymentStatus Status { get; private set; }
 
+    public void SetOrderId(Guid orderId)
+    {
+        if (orderId == Guid.Empty)
+            throw new DomainException("OrderId cannot be empty");
+        OrderId = orderId;
+    }
+
+    public void SetAmount(decimal amount)
+    {
+        if (amount <= 0)
+            throw new DomainException("Amount must be greater than zero");
+        Amount = amount;
+    }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 1 to 4
using Amazon.DynamoDBv2.Model;
using Bmb.Domain.Core.ValueObjects;
using Bmb.Payment.Domain.ValueObjects;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

The Core namespace import can be safely removed

Looking at the full content of Presenter.cs, I can see that no types from Bmb.Domain.Core.ValueObjects are actually being used in the code. The only value objects used are PaymentId, PaymentStatus, and PaymentType, which are all from the Payment domain (Bmb.Payment.Domain.ValueObjects). The Core namespace import is redundant and should be removed to maintain clean domain boundaries.

🔗 Analysis chain

Complete domain separation by moving all value objects to the Domain namespace

The presence of both Bmb.Domain.Core.ValueObjects and Bmb.Payment.Domain.ValueObjects suggests incomplete domain separation. Consider moving all payment-related value objects to the Domain namespace to maintain clean boundaries.


Let me try another verification approach to check the usage of Core namespace and understand the value objects better.


Let me check what specific value objects are being used in the Presenter.cs file to better understand if the Core namespace is actually needed.


Let me check what types are actually being used from the Core namespace.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for remaining Core namespace usage in the codebase
rg -l "Bmb.Domain.Core.ValueObjects" | while read -r file; do
  echo "File still using Core namespace: $file"
  rg -A 2 "Bmb.Domain.Core.ValueObjects" "$file"
done

Length of output: 159


Script:

#!/bin/bash
# Search for Core and Domain value objects usage
echo "Files using Core ValueObjects:"
rg -l "using.*Bmb\.Domain\.Core\.ValueObjects"

echo -e "\nFiles using Payment Domain ValueObjects:"
rg -l "using.*Bmb\.Payment\.Domain\.ValueObjects"

echo -e "\nValue object definitions and usages:"
ast-grep --pattern 'class $_ : ValueObject'

Length of output: 2872


Script:

#!/bin/bash
# Check specific value object usage in Presenter.cs
echo "Value object usage in Presenter.cs:"
rg -A 2 "Core\.ValueObjects|Domain\.ValueObjects" "src/Bmb.Payment.DynamoDb/Presenter.cs"

echo -e "\nFull content of Presenter.cs to understand context:"
cat "src/Bmb.Payment.DynamoDb/Presenter.cs"

Length of output: 2414


Script:

#!/bin/bash
# Search for actual usage of types from Core namespace
echo "Searching for Core value object usage in the code:"
rg -A 5 "new.*Core\.ValueObjects\.|: Core\.ValueObjects\." "src/Bmb.Payment.DynamoDb/Presenter.cs"

echo -e "\nChecking PaymentId definition:"
rg -l "class PaymentId"

Length of output: 312

Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
76.1% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@italopessoa italopessoa merged commit 8902566 into main Nov 17, 2024
5 of 7 checks passed
Copy link

🎉 This PR is included in version 1.4.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@italopessoa italopessoa deleted the split_domain branch December 2, 2024 16:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant