Eternelle-191#192
Conversation
PersonFirstName, PersonLastName, and PersonName moved from Eternelle.Modules.Weddings.Domain.Shared to Eternelle.Common.Domain.People so the upcoming Rsvp module can reference them without violating the cross-module reference rule. All Weddings layers (Domain, Application, Infrastructure) updated with using Eternelle.Common.Domain.People.
…te to Common.Domain.Text
Also fix CA1725 in RsvpDbContext.ConfigureConventions (parameter rename builder→configurationBuilder).
WalkthroughAdds a new RSVP bounded context (domain, application, infrastructure, integration-events, presentation), database schema/migrations, outbox/inbox processing, Dapper read handlers, HTTP endpoints, host wiring (Program + csproj/project refs), rate-limiting policies, and cross-module namespace cleanup and cancellation-token propagation. ChangesRSVP bounded context
Estimated code review effort: 🎯 4 (Complex) | ⏱️ ~60 minutes
Suggested labels: I still can’t generate the full validated hidden artifact in one message. Please confirm that I should split the hidden review artifact into two sequential replies (I will emit the complete artifact across those two messages). ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
✨ Simplify code
|
There was a problem hiding this comment.
Actionable comments posted: 23
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/database-design.md`:
- Line 468: Update the fenced code blocks reported by the linter (near lines
468, 1211, 1226, 1291) to include appropriate language identifiers (e.g.,
```sql, ```json, ```yaml, ```bash as applicable) and ensure there is a blank
line both before and after each fenced block to satisfy markdownlint rules MD040
and MD031; scan the file for other triple-backtick blocks and apply the same
changes so all fenced blocks have a language tag and are separated by blank
lines.
- Around line 21-23: Update the table row for the RSVP module to reflect its
shipped state: change the status cell for the RSVP row (module name "RSVP", slug
"`rsvp`") from "❌ Planned" to a shipped/implemented indicator (e.g., "✅ Shipped"
or "Implemented") and, if present elsewhere (the duplicate row at the same table
block), make the same change so the docs match the delivered "rsvp" module
wiring and schema artifacts; do not alter other columns except the status text.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/AddGuest/AddGuestCommandHandler.cs`:
- Line 27: The handler currently returns a guest-scoped not-found:
Result.Failure<Guid>(GuestErrors.NotFound(GuestId.Empty)) when the wedding is
missing; replace that with a wedding-scoped not-found using the wedding id from
the command, e.g. return
Result.Failure<Guid>(WeddingErrors.NotFound(request.WeddingId)), so the failure
correctly reflects a missing wedding in AddGuestCommandHandler and aligns with
caller semantics.
- Around line 75-95: The current read-before-write loop using
tokenGenerator.GenerateLookupCode() plus guestRepository.LookupCodeExistsAsync
is race-prone and unbounded; change to a bounded-retry insert-on-conflict
strategy: attempt to create the LookupCode and Guest (using LookupCode.Create
and Guest.Create) and call guestRepository.Insert followed by
unitOfWork.SaveChangesAsync inside a retry loop (e.g., maxAttempts constant); on
SaveChangesAsync catch the unique constraint/DB update exception that indicates
a lookup-code collision, discard that LookupCode and retry generating a new one
(up to maxAttempts), and only return failure after attempts are exhausted or if
Guest.Create fails for other reasons. Ensure you reference
tokenGenerator.GenerateLookupCode, LookupCode.Create, Guest.Create,
guestRepository.Insert, and unitOfWork.SaveChangesAsync when implementing the
retry-and-conflict-handling logic.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/GetGuest/GetGuestQueryHandler.cs`:
- Around line 9-23: GetGuestQueryHandler currently uses IGuestRepository and
IRsvpSubmissionRepository; replace those repository reads in Handle with direct
Dapper queries via IDbConnectionFactory using CommandDefinition (including the
incoming cancellationToken). Specifically, inject IDbConnectionFactory into
GetGuestQueryHandler, open a connection, execute a parameterized query to fetch
the guest by query.GuestId and query.WeddingId via Dapper (using new GuestId/new
WeddingId mapping as needed), and execute another parameterized query to fetch
the latest RsvpSubmission for that guest using
CommandDefinition(cancellationToken: cancellationToken) for both calls instead
of guestRepository.GetByIdAsync and submissionRepository.GetLatestByGuestAsync;
finally map results into the same GuestResponse/Guest and latest variables and
return the same Result<GuestResponse> flow.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ListGuests/ListGuestsQuery.cs`:
- Line 11: ListGuestsQuery exposes a Take parameter with default 50 but no upper
bound, allowing callers to request arbitrarily large pages; add validation in
ListGuestsQueryValidator to enforce a sensible range (e.g., 1–100) by adding a
RuleFor(q => q.Take).InclusiveBetween(1, 100).WithMessage("Take must be between
1 and 100."); keep the default value on ListGuestsQuery but ensure the validator
is invoked wherever queries are validated (class names: ListGuestsQuery and
ListGuestsQueryValidator).
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ListGuests/ListGuestsQueryHandler.cs`:
- Around line 9-17: ListGuestsQueryHandler currently loads all guests and
submissions via IGuestRepository.ListByWeddingAsync and
IRsvpSubmissionRepository.ListByWeddingAsync then performs latestByGuest
aggregation, GroupName/IsLocked/AttendingState filtering and Skip/Take in
memory; change Handle to use a Dapper-backed query (via IDbConnectionFactory
creating a connection and executing a CommandDefinition with the
cancellationToken) that performs the join/aggregation to compute latest
submission per guest, applies GroupName/IsLocked/AttendingState filters and SQL
OFFSET/FETCH pagination, and projects results directly into GuestSummaryResponse
to avoid loading all rows into memory; keep method signature and return type the
same but replace the in-memory LINQ steps (including latestByGuest logic) with
the single SQL/Dapper query.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/RegenerateGuestToken/RegenerateGuestTokenCommandHandler.cs`:
- Around line 27-36: The current RegenerateGuestTokenCommandHandler uses an
unbounded loop calling tokenGenerator.GenerateLookupCode() and then uses
LookupCode.Create(raw).Value which can throw; change this to a bounded retry
loop (e.g., maxAttempts constant) that calls tokenGenerator.GenerateLookupCode()
and guestRepository.LookupCodeExistsAsync(raw, cancellationToken) up to the
limit, and if no unused code is found return a failed Result; also call
LookupCode.Create(raw) and check its Result/IsSuccess instead of accessing
.Value, returning a failed Result when Create returns a failure; ensure all
failure paths return Result or Result<T> rather than throwing.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/GetInvitation/GetInvitationQueryHandler.cs`:
- Around line 11-73: The handler currently uses EF-backed repositories
(IGuestRepository, IRsvpSubmissionRepository, IWeddingProjection) inside
GetInvitationQueryHandler; replace those with IDbConnectionFactory and Dapper
calls: validate the LookupCode as before, call await
dbConnectionFactory.OpenConnectionAsync(cancellationToken), create
CommandDefinition instances (always passing cancellationToken) to SELECT the
guest by lookup_code, SELECT the wedding by guest.wedding_id, and SELECT the
latest submission + companions by guest.id using SQL that maps to the existing
DTOs/values; remove repository constructor parameters and use the retrieved rows
to build the same InvitationResponse, SubmissionDetailResponse and
CompanionResponse, and preserve the clock-based isAccepting logic. Ensure all DB
queries use CommandDefinition with cancellationToken and map results to the same
properties used (Guest.Id/WeddingId/FirstName/LastName,
Wedding.WeddingDate/RsvpDeadline, Submission fields and companions).
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/SubmitRsvp/SubmitRsvpCommandHandler.cs`:
- Around line 51-53: PersonName.Create(...) is returning a Result but the code
dereferences .Value directly which can throw if invariants fail; change the
SubmitRsvpCommandHandler to inspect the Result returned by
PersonName.Create($"${guest.FirstName.Value} {guest.LastName.Value}") and handle
the failure branch (e.g., return a failed Result/command response or throw a
controlled exception) instead of accessing .Value; assign nameSnapshot only when
the Result is successful and propagate or log the error otherwise to preserve
the Result-based flow.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetSubmission/GetSubmissionQueryHandler.cs`:
- Around line 8-49: GetSubmissionQueryHandler currently depends on
IRsvpSubmissionRepository and loads the EF aggregate; change it to use
IDbConnectionFactory and Dapper instead: inject IDbConnectionFactory into the
handler, open a connection, create a CommandDefinition (passing the
cancellationToken) with a SQL query that selects the submission row and its
companion rows (join or two queries) using parameters submissionId =
query.SubmissionId and weddingId = query.WeddingId, execute with
QueryAsync/QueryMultipleAsync and map results into the same SubmissionResponse
and List<SubmissionCompanionResponse> shapes, and keep the same null/not-found
check returning RsvpSubmissionErrors.NotFound(new
SubmissionId(query.SubmissionId)) when no rows; ensure you remove the call to
submissionRepository.GetByIdAsync and use CommandDefinition for every Dapper
call so the cancellationToken is passed through.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/ListSubmissions/ListSubmissionsQueryHandler.cs`:
- Around line 14-40: The handler currently loads all submissions via
submissionRepository.ListByWeddingAsync and performs in-memory filtering and
pagination (using query.Attending, query.Source, query.Skip, query.Take) which
must be pushed into the repository; add a repository method (e.g.,
submissionRepository.ListByWeddingAsync or ListByWeddingWithFiltersAsync) that
accepts weddingId plus optional filters for Attending and Source and pagination
parameters Skip/Take, implement the DB-level Where/Skip/Take there, and update
ListSubmissionsQueryHandler to call the new repository method and only map the
returned results into SubmissionSummaryResponse (preserving fields like
s.Id.Value, s.GuestId?.Value, s.NameSnapshot.Value, s.Attending,
s.Companions.Count, s.Source, s.SubmittedAtUtc).
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/SubmitOnBehalf/SubmitOnBehalfCommandHandler.cs`:
- Around line 37-39: The code constructs a PersonName snapshot using
PersonName.Create($"{guest.FirstName.Value} {guest.LastName.Value}").Value which
can throw on invalid input; update SubmitOnBehalfCommandHandler to call
PersonName.Create and check the returned Result (or use a Try/TryParse-style
API) before accessing .Value, and if creation fails return or propagate a
failure Result from the handler instead of letting an exception escape;
reference the PersonName.Create call and the nameSnapshot variable when
implementing the guard so the handler preserves its Result error contract.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Weddings/ClearRsvpDeadline/ClearRsvpDeadlineCommandHandler.cs`:
- Around line 8-22: The handler ClearRsvpDeadlineCommandHandler currently
mutates the aggregate and calls IWeddingProjection.UpdateAsync; replace the
projection write with the domain repository + unit of work pattern: inject
IWeddingRepository and IUnitOfWork instead of IWeddingProjection, use
repository.Update(wedding) after calling wedding.SetRsvpDeadline(...), then call
await unitOfWork.SaveChangesAsync(cancellationToken) before returning; remove
the call to projection.UpdateAsync and ensure null-wedding handling and Result
return remain the same.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Weddings/SetRsvpDeadline/SetRsvpDeadlineCommandHandler.cs`:
- Around line 8-25: Replace use of IWeddingProjection with the domain repository
and unit-of-work pattern: change the handler constructor to accept
IWeddingRepository weddingRepository and IUnitOfWork unitOfWork (keep
IDateTimeProvider clock), fetch the aggregate via weddingRepository.GetAsync(new
WeddingId(command.WeddingId), ...), perform the same null check and call
wedding.SetRsvpDeadline(command.Deadline, clock.UtcNow), then call
weddingRepository.Update(wedding) and finally await
unitOfWork.SaveChangesAsync(cancellationToken) instead of calling
projection.UpdateAsync; update method/signature names accordingly
(SetRsvpDeadlineCommandHandler, weddingRepository.GetAsync,
weddingRepository.Update, unitOfWork.SaveChangesAsync).
In `@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/Guest.cs`:
- Around line 15-19: The properties FirstName, LastName and LookupCode use
null-forgiving (!) suppressions without justification; update the Guest
aggregate (properties FirstName, LastName, LookupCode in Guest.cs) to include an
inline comment explaining the nullable suppression reason (for example:
"initialized by EF Core during materialization" or equivalent) next to each =
null! initializer so it complies with project nullable policy and makes the
intent explicit.
In `@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Weddings/WeddingId.cs`:
- Around line 3-7: Add a static New() factory to the WeddingId record so it
matches GuestId/SubmissionId patterns; implement static WeddingId New() that
returns a new WeddingId seeded with a UUIDv7 using the project's UUIDv7
generator (e.g., Uuid.NewV7() or GuidHelpers.NewV7()), keep the existing Empty
property and ToString() intact so callers can create consistent, strongly-typed
IDs via WeddingId.New().
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Inbox/ConfigureProcessInboxJob.cs`:
- Around line 20-21: The schedule builder in ConfigureProcessInboxJob uses
WithIntervalInSeconds(_inboxOptions.IntervalInSeconds) without validating
non-positive values; add a guard before building the trigger in
ConfigureProcessInboxJob (or its enclosing method/constructor) to ensure
_inboxOptions.IntervalInSeconds > 0 and either throw a clear ArgumentException
or use a safe default, so the .WithSimpleSchedule(...) call never receives 0 or
negative seconds; reference the _inboxOptions.IntervalInSeconds field and the
schedule-building code that calls WithIntervalInSeconds to locate where to add
the check.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Outbox/ProcessOutboxJob.cs`:
- Around line 74-77: GetOutboxMessagesAsync and UpdateOutboxMessageAsync
currently call Dapper QueryAsync/ExecuteAsync without a CancellationToken, so
propagate the Quartz cancellation by adding a CancellationToken parameter to
these helpers (and to Execute where it calls them) and use Dapper's
CommandDefinition with cancellationToken: context.CancellationToken when calling
QueryAsync/ExecuteAsync; specifically update GetOutboxMessagesAsync and
UpdateOutboxMessageAsync signatures to accept a CancellationToken, thread
context.CancellationToken from Execute into those calls, and construct
CommandDefinition instances for the SQL executions to pass the token to Dapper.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Submissions/RsvpSubmissionConfiguration.cs`:
- Around line 59-64: The HasMaxLength(45) call in RsvpSubmissionConfiguration
for the IpAddress property uses a magic number; add or use the domain
value-object constant (e.g., on the IP address VO such as IpAddress.MaxLength or
RsvpIpAddress.MaxLength) and replace the literal 45 with that constant in the
HasMaxLength call inside the RsvpSubmissionConfiguration class; ensure the
constant is public on the owning domain type so the EF configuration can
reference it.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Weddings/WeddingProjection.cs`:
- Around line 12-19: UpsertAsync in WeddingProjection no-ops when an existing
Wedding is found; update the tracked entity with the incoming wedding's values
before SaveChangesAsync. Locate the UpsertAsync method and, when existing !=
null, apply the incoming wedding onto the tracked entity (for example by mapping
properties or using context.Entry(existing).CurrentValues.SetValues(wedding)) so
fields are updated, then call await context.SaveChangesAsync(cancellationToken);
ensure you preserve the existing entity's key and navigation tracking rather
than replacing the tracked instance.
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Submissions/ListSubmissionsEndpoint.cs`:
- Line 31: The current parsing in ListSubmissionsEndpoint.cs uses
Enum.TryParse<RsvpSource> which will accept numeric strings that don't map to
declared members; change the logic around the RsvpSource parsing so you still
TryParse into the local variable (s) but only assign parsedSource = s if
Enum.IsDefined(typeof(RsvpSource), s) returns true, otherwise set parsedSource
to null; update the single expression that defines parsedSource (or the
surrounding block) to perform both TryParse and Enum.IsDefined checks using the
RsvpSource type and the local variable name s.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3fccc054-60b9-4ce1-9d4a-090ec8a2b382
📒 Files selected for processing (223)
Eternelle.slnxdocs/database-design.mdsrc/API/Eternelle.Api/Eternelle.Api.csprojsrc/API/Eternelle.Api/Extensions/MigrationExtensions.cssrc/API/Eternelle.Api/Program.cssrc/API/Eternelle.Api/modules.rsvp.Development.jsonsrc/API/Eternelle.Api/modules.rsvp.jsonsrc/Common/Eternelle.Common.Domain/AssemblyInfo.cssrc/Common/Eternelle.Common.Domain/People/EmailAddress.cssrc/Common/Eternelle.Common.Domain/People/EmailAddressErrors.cssrc/Common/Eternelle.Common.Domain/People/PersonFirstName.cssrc/Common/Eternelle.Common.Domain/People/PersonFirstNameErrors.cssrc/Common/Eternelle.Common.Domain/People/PersonLastName.cssrc/Common/Eternelle.Common.Domain/People/PersonLastNameErrors.cssrc/Common/Eternelle.Common.Domain/People/PersonName.cssrc/Common/Eternelle.Common.Domain/People/PersonNameErrors.cssrc/Common/Eternelle.Common.Domain/People/PhoneNumber.cssrc/Common/Eternelle.Common.Domain/People/PhoneNumberErrors.cssrc/Common/Eternelle.Common.Domain/Text/GroupLabel.cssrc/Common/Eternelle.Common.Domain/Text/GroupLabelErrors.cssrc/Common/Eternelle.Common.Domain/Text/InternalNote.cssrc/Common/Eternelle.Common.Domain/Text/InternalNoteErrors.cssrc/Common/Eternelle.Common.Domain/Text/RichDescription.cssrc/Common/Eternelle.Common.Domain/Text/RichDescriptionErrors.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Abstractions/Data/IUnitOfWork.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Abstractions/Security/ITokenGenerator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/AssemblyReference.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Eternelle.Modules.Rsvp.Application.csprojsrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/AddGuest/AddGuestCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/AddGuest/AddGuestCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/AddGuest/AddGuestCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ChangeGuestAllowance/ChangeGuestAllowanceCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ChangeGuestAllowance/ChangeGuestAllowanceCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ChangeGuestAllowance/ChangeGuestAllowanceCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/GetGuest/GetGuestQuery.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/GetGuest/GetGuestQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/GetGuest/GuestResponse.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/GuestAddedDomainEventHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ListGuests/GuestSummaryResponse.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ListGuests/ListGuestsQuery.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ListGuests/ListGuestsQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/LockGuest/LockGuestCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/LockGuest/LockGuestCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/LockGuest/LockGuestCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/RegenerateGuestToken/RegenerateGuestTokenCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/RegenerateGuestToken/RegenerateGuestTokenCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/RegenerateGuestToken/RegenerateGuestTokenCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/RemoveGuest/RemoveGuestCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/RemoveGuest/RemoveGuestCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/RemoveGuest/RemoveGuestCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/UnlockGuest/UnlockGuestCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/UnlockGuest/UnlockGuestCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/UnlockGuest/UnlockGuestCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/UpdateGuestContact/UpdateGuestContactCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/UpdateGuestContact/UpdateGuestContactCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/UpdateGuestContact/UpdateGuestContactCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/UpdateGuestPlacement/UpdateGuestPlacementCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/UpdateGuestPlacement/UpdateGuestPlacementCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/UpdateGuestPlacement/UpdateGuestPlacementCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/GetInvitation/GetInvitationQuery.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/GetInvitation/GetInvitationQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/GetInvitation/InvitationResponse.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/SubmitRsvp/SubmitRsvpCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/SubmitRsvp/SubmitRsvpCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/SubmitRsvp/SubmitRsvpCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetHeadcount/GetHeadcountQuery.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetHeadcount/GetHeadcountQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetHeadcount/HeadcountResponse.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetSubmission/GetSubmissionQuery.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetSubmission/GetSubmissionQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetSubmission/SubmissionResponse.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/ListSubmissions/ListSubmissionsQuery.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/ListSubmissions/ListSubmissionsQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/ListSubmissions/SubmissionSummaryResponse.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/RsvpSubmittedDomainEventHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/SubmitOnBehalf/SubmitOnBehalfCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/SubmitOnBehalf/SubmitOnBehalfCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/SubmitOnBehalf/SubmitOnBehalfCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Weddings/ClearRsvpDeadline/ClearRsvpDeadlineCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Weddings/ClearRsvpDeadline/ClearRsvpDeadlineCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Weddings/ClearRsvpDeadline/ClearRsvpDeadlineCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Weddings/SetRsvpDeadline/SetRsvpDeadlineCommand.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Weddings/SetRsvpDeadline/SetRsvpDeadlineCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Weddings/SetRsvpDeadline/SetRsvpDeadlineCommandValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/AssemblyInfo.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Eternelle.Modules.Rsvp.Domain.csprojsrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/Guest.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/GuestAddedDomainEvent.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/GuestAllowanceChangedDomainEvent.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/GuestContactUpdatedDomainEvent.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/GuestErrors.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/GuestId.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/GuestLockedDomainEvent.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/GuestLookupCodeRegeneratedDomainEvent.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/GuestPlacementUpdatedDomainEvent.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/GuestRemovedDomainEvent.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/GuestUnlockedDomainEvent.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/IGuestRepository.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Shared/DietaryNote.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Shared/DietaryNoteErrors.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Shared/LookupCode.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Shared/LookupCodeErrors.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Submissions/CompanionId.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Submissions/IRsvpSubmissionRepository.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Submissions/RsvpSource.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Submissions/RsvpSubmission.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Submissions/RsvpSubmissionErrors.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Submissions/RsvpSubmittedDomainEvent.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Submissions/SubmissionCompanion.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Submissions/SubmissionId.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Weddings/IWeddingProjection.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Weddings/Wedding.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Weddings/WeddingId.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Database/Converters/CompanionIdConverter.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Database/Converters/GuestIdConverter.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Database/Converters/SubmissionIdConverter.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Database/Converters/WeddingIdConverter.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Database/RsvpDbContext.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Database/RsvpDbContextFactory.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Database/Schemas.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Eternelle.Modules.Rsvp.Infrastructure.csprojsrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Guests/GuestConfiguration.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Guests/GuestRepository.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Inbox/ConfigureProcessInboxJob.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Inbox/InboxOptions.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Inbox/IntegrationEventConsumer.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Inbox/ProcessInboxJob.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Migrations/20260529094037_InitialRsvp.Designer.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Migrations/20260529094037_InitialRsvp.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Migrations/RsvpDbContextModelSnapshot.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Outbox/ConfigureProcessOutboxJob.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Outbox/IdempotentDomainEventHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Outbox/OutboxOptions.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Outbox/ProcessOutboxJob.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/RsvpModule.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Security/LookupCodeGenerator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Submissions/RsvpSubmissionConfiguration.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Submissions/RsvpSubmissionRepository.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Weddings/WeddingConfiguration.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Weddings/WeddingProjection.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.IntegrationEvents/Eternelle.Modules.Rsvp.IntegrationEvents.csprojsrc/Modules/Rsvp/Eternelle.Modules.Rsvp.IntegrationEvents/Guests/GuestAddedIntegrationEvent.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.IntegrationEvents/Submissions/RsvpSubmittedIntegrationEvent.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/AssemblyReference.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Eternelle.Modules.Rsvp.Presentation.csprojsrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Guests/AddGuestEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Guests/ChangeGuestAllowanceEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Guests/GetGuestEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Guests/ListGuestsEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Guests/LockGuestEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Guests/RegenerateGuestTokenEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Guests/RemoveGuestEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Guests/UnlockGuestEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Guests/UpdateGuestContactEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Guests/UpdateGuestPlacementEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Public/GetInvitationEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Public/SubmitRsvpEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/RateLimitingPolicies.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Submissions/GetHeadcountEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Submissions/GetSubmissionEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Submissions/ListSubmissionsEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Submissions/SubmitOnBehalfEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Tags.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Weddings/ClearRsvpDeadlineEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Weddings/SetRsvpDeadlineEndpoint.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Weddings/WeddingCreatedIntegrationEventHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/CeremonyActs/CreateCeremonyAct/CreateCeremonyActCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/CeremonyActs/CreateCeremonyAct/CreateCeremonyActCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/CeremonyActs/UpdateCeremonyAct/UpdateCeremonyActCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/CeremonyActs/UpdateCeremonyAct/UpdateCeremonyActCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/DressCodeConfigs/CreateDressCodeConfig/CreateDressCodeConfigCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/DressCodeConfigs/CreateDressCodeConfig/CreateDressCodeConfigCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/DressCodeConfigs/UpdateDressCodeConfig/UpdateDressCodeConfigCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/DressCodeConfigs/UpdateDressCodeConfig/UpdateDressCodeConfigCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/EntourageGroups/AddEntourageMember/AddEntourageMemberCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/EntourageGroups/AddEntourageMember/AddEntourageMemberCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/EntourageGroups/CreateEntourageGroup/CreateEntourageGroupCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/EntourageGroups/CreateEntourageGroup/CreateEntourageGroupCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/EntourageGroups/PairEntourageCouple/PairEntourageCoupleCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/EntourageGroups/UpdateEntourageGroup/UpdateEntourageGroupCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/EntourageGroups/UpdateEntourageGroup/UpdateEntourageGroupCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/EntourageGroups/UpdateEntourageMember/UpdateEntourageMemberCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/EntourageGroups/UpdateEntourageMember/UpdateEntourageMemberCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GiftOptions/CreateGiftOption/CreateGiftOptionCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GiftOptions/CreateGiftOption/CreateGiftOptionCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GiftOptions/UpdateGiftOption/UpdateGiftOptionCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GiftOptions/UpdateGiftOption/UpdateGiftOptionCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GuestPhotos/RegisterGuestPhotos/RegisterGuestPhotosCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GuestPhotos/RegisterGuestPhotos/RegisterGuestPhotosCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Reminders/CreateReminder/CreateReminderCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Reminders/CreateReminder/CreateReminderCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Reminders/UpdateReminder/UpdateReminderCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Reminders/UpdateReminder/UpdateReminderCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/StoryMoments/CreateStoryMoment/CreateStoryMomentCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/StoryMoments/CreateStoryMoment/CreateStoryMomentCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/StoryMoments/UpdateStoryMoment/UpdateStoryMomentCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/StoryMoments/UpdateStoryMoment/UpdateStoryMomentCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Weddings/AddPartner/AddPartnerCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Weddings/AddPartner/AddPartnerCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Weddings/UpdatePartner/UpdatePartnerCommandHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Weddings/UpdatePartner/UpdatePartnerCommandValidator.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Domain/CeremonyActs/CeremonyAct.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Domain/DressCodeConfigs/DressCodeConfig.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Domain/EntourageGroups/EntourageCouple.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Domain/EntourageGroups/EntourageGroup.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Domain/EntourageGroups/EntourageMember.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Domain/GiftOptions/GiftOption.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Domain/GuestPhotos/GuestPhoto.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Domain/Reminders/Reminder.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Domain/StoryMoments/StoryMoment.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Domain/Weddings/Partner.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Domain/Weddings/Wedding.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/CeremonyActs/CeremonyActConfiguration.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/DressCodeConfigs/DressCodeConfigConfiguration.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/EntourageGroups/EntourageCoupleConfiguration.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/EntourageGroups/EntourageGroupConfiguration.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/EntourageGroups/EntourageMemberConfiguration.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/GiftOptions/GiftOptionConfiguration.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/GuestPhotos/GuestPhotoConfiguration.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/Reminders/ReminderConfiguration.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/StoryMoments/StoryMomentConfiguration.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/Weddings/WeddingConfiguration.cs
Changed Tier, PriceClass, and SectionId columns from string to integer types in migration and model snapshot files. Removed SQL data seeding from migration and made minor formatting and namespace adjustments.
Replace the stale database-design.md draft with a system-level architecture.md covering runtime topology, the shared kernel, the synchronous request lifecycle, the async outbox/inbox messaging pipeline, a corrected cross-module data view (shipped schemas only) + integration-event map, cross-cutting concerns, and architecture decisions. Repoint references in CLAUDE.md, README.md, Eternelle.slnx, and docs/modules/README.md.
Removed the weddings inbox/outbox alignment plan from the solution. Added warning-level log filters for Catalog, Tenancy, and Rsvp module Outbox and Inbox components in development settings.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/architecture.md`:
- Around line 99-100: Add missing blank lines around subsection headings and
tables: ensure there is a blank line before each subsection heading like "###
`Common.Domain`" and add blank lines above and below each markdown table and
code block throughout the document (sections referenced: headings and tables
around `Common.Domain` and the listed ranges). Also update the closing code
fence that currently reads ```text to ```markdown to explicitly set the language
for that fenced block; verify every fenced block has a blank line before the
opening fence and before the closing fence. Fix the noted occurrences (including
the one with "### `Common.Domain`" and the fenced block using ```text)
consistently across the file.
- Around line 264-275: The docs list for WeddingCreatedIntegrationEvent is
stale; update the Integration-event map row for WeddingCreatedIntegrationEvent
to show RSVP as a consumer (since RsvpModule and
WeddingCreatedIntegrationEventHandler already handle it) by replacing “(none
yet)” with “RSVP” (or, if you prefer the repo-main-only doc rule, add an
explicit parenthetical note stating RSVP handling exists in RsvpModule /
WeddingCreatedIntegrationEventHandler but is pending merge); ensure you
reference the event name WeddingCreatedIntegrationEvent and the consumer module
identifiers RsvpModule and WeddingCreatedIntegrationEventHandler so readers can
find the implementation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 0d9070a6-96e8-4c13-a79d-aab06c9f0216
📒 Files selected for processing (6)
CLAUDE.mdEternelle.slnxREADME.mddocs/architecture.mddocs/modules/README.mdsrc/API/Eternelle.Api/appsettings.Development.json
Refactored RSVP module query handlers to use Dapper and direct SQL via IDbConnectionFactory, replacing repository/domain model access for guest and submission queries. Improved error handling, added query validation, and updated ProcessOutboxJob for cancellation support. Enhanced enum validation, upsert logic, and documentation. Introduced WeddingErrors for consistent error reporting.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/GetInvitation/GetInvitationQueryHandler.cs`:
- Line 26: GetInvitationQueryHandler currently calls
IDbConnectionFactory.OpenConnectionAsync() without a CancellationToken but the
interface/DbConnectionFactory lack a CancellationToken overload; add a new
OpenConnectionAsync(CancellationToken) method to IDbConnectionFactory and
implement it in DbConnectionFactory (and any other implementations), then update
GetInvitationQueryHandler to call OpenConnectionAsync(cancellationToken) so
connection acquisition can be cancelled while leaving existing Dapper
CommandDefinition cancellation handling unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: d8686371-3d90-4578-a9b4-d88a6cfe3b3f
📒 Files selected for processing (19)
Eternelle.slnxdocs/architecture.mdsrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/AddGuest/AddGuestCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/GetGuest/GetGuestQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ListGuests/ListGuestsQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ListGuests/ListGuestsQueryValidator.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/RegenerateGuestToken/RegenerateGuestTokenCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/GetInvitation/GetInvitationQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetHeadcount/GetHeadcountQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetSubmission/GetSubmissionQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/ListSubmissions/ListSubmissionsQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/SubmitOnBehalf/SubmitOnBehalfCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Guests/Guest.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Submissions/RsvpSubmission.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Domain/Weddings/WeddingErrors.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Outbox/ProcessOutboxJob.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Submissions/RsvpSubmissionConfiguration.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Weddings/WeddingProjection.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Presentation/Submissions/ListSubmissionsEndpoint.cs
Consistently pass CancellationToken to all async DB connections by updating IDbConnectionFactory and all usages. Replace some .ToList() calls with spread operator for brevity. Fix PersonName creation error handling in SubmitRsvpCommandHandler.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/Modules/Weddings/Eternelle.Modules.Weddings.Application/Weddings/GetWedding/GetWeddingQueryHandler.cs`:
- Line 15: The multi-mapping Dapper call in GetWeddingQueryHandler uses
connection.QueryAsync(...) without a CommandDefinition, so the query isn’t
cancellable; change the call to use new CommandDefinition(sql, query,
cancellationToken: cancellationToken) (or equivalent) when invoking
connection.QueryAsync to pass the cancellationToken through, keeping the same
sql and query parameters and preserving the multi-mapping delegate and splitOn
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 60c6a333-6628-4362-9906-752e6a99f479
📒 Files selected for processing (38)
src/Common/Eternelle.Common.Application/Data/IDbConnectionFactory.cssrc/Common/Eternelle.Common.Infrastructure/Data/DbConnectionFactory.cssrc/Modules/Catalog/Eternelle.Modules.Catalog.Application/Templates/GetSectionVariants/GetSectionVariantsQueryHandler.cssrc/Modules/Catalog/Eternelle.Modules.Catalog.Application/Templates/GetSupportedSections/GetSupportedSectionsQueryHandler.cssrc/Modules/Catalog/Eternelle.Modules.Catalog.Application/Templates/GetTemplateById/GetTemplateByIdQueryHandler.cssrc/Modules/Catalog/Eternelle.Modules.Catalog.Application/Templates/GetTemplates/GetTemplatesQueryHandler.cssrc/Modules/Catalog/Eternelle.Modules.Catalog.Infrastructure/Outbox/IdempotentDomainEventHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/GetGuest/GetGuestQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ListGuests/ListGuestsQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/GetInvitation/GetInvitationQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/SubmitRsvp/SubmitRsvpCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetHeadcount/GetHeadcountQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetSubmission/GetSubmissionQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/ListSubmissions/ListSubmissionsQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Outbox/IdempotentDomainEventHandler.cssrc/Modules/Tenancy/Eternelle.Modules.Tenancy.Application/Tenants/GetTenant/GetTenantQueryHandler.cssrc/Modules/Tenancy/Eternelle.Modules.Tenancy.Application/Tenants/GetTenantSections/GetTenantSectionsQueryHandler.cssrc/Modules/Tenancy/Eternelle.Modules.Tenancy.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cssrc/Modules/Tenancy/Eternelle.Modules.Tenancy.Infrastructure/Outbox/IdempotentDomainEventHandler.cssrc/Modules/Users/Eternelle.Modules.Users.Application/Users/GetUser/GetUserQueryHandler.cssrc/Modules/Users/Eternelle.Modules.Users.Application/Users/GetUserPermissions/GetUserPermissionsQueryHandler.cssrc/Modules/Users/Eternelle.Modules.Users.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cssrc/Modules/Users/Eternelle.Modules.Users.Infrastructure/Outbox/IdempotentDomainEventHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/CeremonyActs/GetCeremonyActs/GetCeremonyActsQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/DressCodeConfigs/GetDressCodeConfig/GetDressCodeConfigQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/EntourageGroups/GetEntourageGroups/GetEntourageGroupsQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GalleryImages/GetGalleryImages/GetGalleryImagesQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GiftOptions/GetGiftOptions/GetGiftOptionsQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GuestPhotos/GetGuestPhotos/GetGuestPhotosQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GuestPhotos/GetPublicGuestPhotos/GetPublicGuestPhotosQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GuestPhotos/GetUploadContext/GetUploadContextQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Reminders/GetReminders/GetRemindersQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/StoryMoments/GetStoryMoments/GetStoryMomentsQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/VendorCredits/GetVendorCredits/GetVendorCreditsQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Weddings/GetWedding/GetWeddingQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/Outbox/IdempotentDomainEventHandler.cs
Updated QueryAsync to use CommandDefinition, ensuring proper parameter handling and support for cancellation tokens during query execution. This improves reliability and consistency in database operations.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/Modules/Tenancy/Eternelle.Modules.Tenancy.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cs (1)
19-30:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winPropagate cancellationToken to Dapper operations.
Same issue as in
Users.Infrastructure.IdempotentIntegrationEventHandler: thecancellationTokenis passed toOpenConnectionAsyncbut not to the DapperExecuteScalarAsync(line 47) andExecuteAsync(line 60) operations. For consistent cancellation support, wrap these inCommandDefinitionand pass the token.Proposed fix
Update the helper method signatures to accept
CancellationToken:private static async Task<bool> InboxConsumerExistsAsync( DbConnection dbConnection, - InboxMessageConsumer inboxMessageConsumer) + InboxMessageConsumer inboxMessageConsumer, + CancellationToken cancellationToken)private static async Task InsertInboxConsumerAsync( DbConnection dbConnection, - InboxMessageConsumer inboxMessageConsumer) + InboxMessageConsumer inboxMessageConsumer, + CancellationToken cancellationToken)Then use
CommandDefinition:- return await dbConnection.ExecuteScalarAsync<bool>(sql, inboxMessageConsumer); + return await dbConnection.ExecuteScalarAsync<bool>( + new CommandDefinition(sql, inboxMessageConsumer, cancellationToken: cancellationToken));- await dbConnection.ExecuteAsync(sql, inboxMessageConsumer); + await dbConnection.ExecuteAsync( + new CommandDefinition(sql, inboxMessageConsumer, cancellationToken: cancellationToken));Update callsites at lines 23 and 30 to pass
cancellationToken.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Modules/Tenancy/Eternelle.Modules.Tenancy.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cs` around lines 19 - 30, The Dapper calls in IdempotentIntegrationEventHandler are not honoring cancellation; update the helper methods InboxConsumerExistsAsync and InsertInboxConsumerAsync to accept a CancellationToken parameter, and inside each method construct and use Dapper.CommandDefinition (or equivalent) that includes the CancellationToken for ExecuteScalarAsync/ExecuteAsync calls; then pass the cancellationToken from the caller when invoking InboxConsumerExistsAsync(...) and InsertInboxConsumerAsync(...). Ensure you reference the existing inboxMessageConsumer parameter and the opened DbConnection when building the CommandDefinition so the correct SQL, parameters and connection are used.src/Modules/Users/Eternelle.Modules.Users.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cs (1)
19-30:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winPropagate cancellationToken to Dapper operations.
The
cancellationTokenis passed toOpenConnectionAsyncbut not toExecuteScalarAsync(line 47) andExecuteAsync(line 60). For consistent cancellation support, pass the token to all async operations.Proposed fix
- return await dbConnection.ExecuteScalarAsync<bool>(sql, inboxMessageConsumer); + return await dbConnection.ExecuteScalarAsync<bool>( + new CommandDefinition(sql, inboxMessageConsumer, cancellationToken: cancellationToken));And similarly for
InsertInboxConsumerAsync:- await dbConnection.ExecuteAsync(sql, inboxMessageConsumer); + await dbConnection.ExecuteAsync( + new CommandDefinition(sql, inboxMessageConsumer, cancellationToken: cancellationToken));Note: These methods need access to the
cancellationToken, so update their signatures:private static async Task<bool> InboxConsumerExistsAsync( DbConnection dbConnection, - InboxMessageConsumer inboxMessageConsumer) + InboxMessageConsumer inboxMessageConsumer, + CancellationToken cancellationToken)private static async Task InsertInboxConsumerAsync( DbConnection dbConnection, - InboxMessageConsumer inboxMessageConsumer) + InboxMessageConsumer inboxMessageConsumer, + CancellationToken cancellationToken)Then update the callsites at lines 23 and 30 to pass
cancellationToken.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Modules/Users/Eternelle.Modules.Users.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cs` around lines 19 - 30, The Dapper calls are not propagating cancellation even though OpenConnectionAsync receives cancellationToken; update the InboxConsumerExistsAsync and InsertInboxConsumerAsync methods to accept a CancellationToken parameter and pass that token into the Dapper async calls (e.g., ExecuteScalarAsync/ExecuteAsync) inside those methods; then update the caller in IdempotentIntegrationEventHandler to pass the same cancellationToken when invoking InboxConsumerExistsAsync(inboxMessageConsumer, cancellationToken) and InsertInboxConsumerAsync(inboxMessageConsumer, cancellationToken), leaving the decorated.Handle(integrationEvent, cancellationToken) call as-is so all async operations honor cancellation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In
`@src/Modules/Tenancy/Eternelle.Modules.Tenancy.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cs`:
- Around line 19-30: The Dapper calls in IdempotentIntegrationEventHandler are
not honoring cancellation; update the helper methods InboxConsumerExistsAsync
and InsertInboxConsumerAsync to accept a CancellationToken parameter, and inside
each method construct and use Dapper.CommandDefinition (or equivalent) that
includes the CancellationToken for ExecuteScalarAsync/ExecuteAsync calls; then
pass the cancellationToken from the caller when invoking
InboxConsumerExistsAsync(...) and InsertInboxConsumerAsync(...). Ensure you
reference the existing inboxMessageConsumer parameter and the opened
DbConnection when building the CommandDefinition so the correct SQL, parameters
and connection are used.
In
`@src/Modules/Users/Eternelle.Modules.Users.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cs`:
- Around line 19-30: The Dapper calls are not propagating cancellation even
though OpenConnectionAsync receives cancellationToken; update the
InboxConsumerExistsAsync and InsertInboxConsumerAsync methods to accept a
CancellationToken parameter and pass that token into the Dapper async calls
(e.g., ExecuteScalarAsync/ExecuteAsync) inside those methods; then update the
caller in IdempotentIntegrationEventHandler to pass the same cancellationToken
when invoking InboxConsumerExistsAsync(inboxMessageConsumer, cancellationToken)
and InsertInboxConsumerAsync(inboxMessageConsumer, cancellationToken), leaving
the decorated.Handle(integrationEvent, cancellationToken) call as-is so all
async operations honor cancellation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: fdba92e0-5124-4efc-b13b-e8e3ab76a4c4
📒 Files selected for processing (38)
src/Common/Eternelle.Common.Application/Data/IDbConnectionFactory.cssrc/Common/Eternelle.Common.Infrastructure/Data/DbConnectionFactory.cssrc/Modules/Catalog/Eternelle.Modules.Catalog.Application/Templates/GetSectionVariants/GetSectionVariantsQueryHandler.cssrc/Modules/Catalog/Eternelle.Modules.Catalog.Application/Templates/GetSupportedSections/GetSupportedSectionsQueryHandler.cssrc/Modules/Catalog/Eternelle.Modules.Catalog.Application/Templates/GetTemplateById/GetTemplateByIdQueryHandler.cssrc/Modules/Catalog/Eternelle.Modules.Catalog.Application/Templates/GetTemplates/GetTemplatesQueryHandler.cssrc/Modules/Catalog/Eternelle.Modules.Catalog.Infrastructure/Outbox/IdempotentDomainEventHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/GetGuest/GetGuestQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Guests/ListGuests/ListGuestsQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/GetInvitation/GetInvitationQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Public/SubmitRsvp/SubmitRsvpCommandHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetHeadcount/GetHeadcountQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/GetSubmission/GetSubmissionQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Application/Submissions/ListSubmissions/ListSubmissionsQueryHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cssrc/Modules/Rsvp/Eternelle.Modules.Rsvp.Infrastructure/Outbox/IdempotentDomainEventHandler.cssrc/Modules/Tenancy/Eternelle.Modules.Tenancy.Application/Tenants/GetTenant/GetTenantQueryHandler.cssrc/Modules/Tenancy/Eternelle.Modules.Tenancy.Application/Tenants/GetTenantSections/GetTenantSectionsQueryHandler.cssrc/Modules/Tenancy/Eternelle.Modules.Tenancy.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cssrc/Modules/Tenancy/Eternelle.Modules.Tenancy.Infrastructure/Outbox/IdempotentDomainEventHandler.cssrc/Modules/Users/Eternelle.Modules.Users.Application/Users/GetUser/GetUserQueryHandler.cssrc/Modules/Users/Eternelle.Modules.Users.Application/Users/GetUserPermissions/GetUserPermissionsQueryHandler.cssrc/Modules/Users/Eternelle.Modules.Users.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cssrc/Modules/Users/Eternelle.Modules.Users.Infrastructure/Outbox/IdempotentDomainEventHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/CeremonyActs/GetCeremonyActs/GetCeremonyActsQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/DressCodeConfigs/GetDressCodeConfig/GetDressCodeConfigQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/EntourageGroups/GetEntourageGroups/GetEntourageGroupsQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GalleryImages/GetGalleryImages/GetGalleryImagesQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GiftOptions/GetGiftOptions/GetGiftOptionsQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GuestPhotos/GetGuestPhotos/GetGuestPhotosQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GuestPhotos/GetPublicGuestPhotos/GetPublicGuestPhotosQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/GuestPhotos/GetUploadContext/GetUploadContextQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Reminders/GetReminders/GetRemindersQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/StoryMoments/GetStoryMoments/GetStoryMomentsQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/VendorCredits/GetVendorCredits/GetVendorCreditsQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Application/Weddings/GetWedding/GetWeddingQueryHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/Inbox/IdempotentIntegrationEventHandler.cssrc/Modules/Weddings/Eternelle.Modules.Weddings.Infrastructure/Outbox/IdempotentDomainEventHandler.cs
Summary by CodeRabbit
New Features
Improvements