Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# ===== .NET / IDE =====
bin/
obj/
.vs/
*.user
*.suo
*.userprefs
.idea/
*.DotSettings.user

# тесты / артефакты
TestResults/
*.log

# ОС
.DS_Store
Thumbs.db

# секреты/локальные конфиги
appsettings.Development.json
appsettings.*.local.json

# docker локальные переопределения
docker-compose.override.yml
20 changes: 20 additions & 0 deletions CoworkingBooking.BookingService.Api/Contracts/BookingMessages.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// CoworkingBooking.BookingService.Api/Contracts/BookingMessages.cs
namespace CoworkingBooking.BookingService.Api.Contracts;

/// <summary>
/// Команда: старт процесса бронирования (запуск саги)
/// </summary>
public sealed record StartBooking(
Guid BookingId,
Guid RoomId,
string UserEmail,
string UserName
);

/// <summary>
/// Событие: комната успешно зарезервирована (переход саги на следующий шаг)
/// </summary>
public sealed record RoomReserved(
Guid BookingId,
string ReservationId
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Threading.Tasks;
using MassTransit;
using CoworkingBooking.BookingService.Api.Coordinator;
using CoworkingBooking.BookingService.Api.Coordinator.Status;

namespace CoworkingBooking.BookingService.Api.Coordinator.Consumers;

public class ReserveRoomConsumer : IConsumer<BookingRequested>
{
private readonly ICoordStatusStore _status;

public ReserveRoomConsumer(ICoordStatusStore status) => _status = status;

public async Task Consume(ConsumeContext<BookingRequested> ctx)
{
var msg = ctx.Message;

var reservationId = $"R-{Random.Shared.Next(100000, 999999)}";

_status.Set(msg.BookingId, $"RoomReserved:{reservationId}");

await ctx.Publish(new RoomReserved(msg.BookingId, reservationId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Threading.Tasks;
using MassTransit;
using CoworkingBooking.BookingService.Api.Coordinator;
using CoworkingBooking.BookingService.Api.Coordinator.Status;
using CoworkingBooking.BookingService.Infrastructure.Integration;

namespace CoworkingBooking.BookingService.Api.Coordinator.Consumers;

public class SendNotifyConsumer : IConsumer<RoomReserved>
{
private readonly INotificationClient _notify;
private readonly ICoordStatusStore _status;

public SendNotifyConsumer(INotificationClient notify, ICoordStatusStore status)
{
_notify = notify;
_status = status;
}

public async Task Consume(ConsumeContext<RoomReserved> ctx)
{
await _notify.PingAsync();

_status.Set(ctx.Message.BookingId, "Notified");

await ctx.Publish(new NotifySent(ctx.Message.BookingId));
}
}
8 changes: 8 additions & 0 deletions CoworkingBooking.BookingService.Api/Coordinator/Messages.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System;

namespace CoworkingBooking.BookingService.Api.Coordinator;

// События/команды для варианта "координатор" (choreography)
public record BookingRequested(Guid BookingId, Guid RoomId, string UserEmail, string UserName);
public record RoomReserved(Guid BookingId, string ReservationId);
public record NotifySent(Guid BookingId);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace CoworkingBooking.BookingService.Api.Coordinator.Status;

public interface ICoordStatusStore
{
void Set(Guid bookingId, string status);
bool TryGet(Guid bookingId, out string status);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Concurrent;

namespace CoworkingBooking.BookingService.Api.Coordinator.Status;

public class InMemoryCoordStatusStore : ICoordStatusStore
{
private readonly ConcurrentDictionary<Guid, string> _store = new();

public void Set(Guid bookingId, string status) => _store[bookingId] = status;

public bool TryGet(Guid bookingId, out string status) => _store.TryGetValue(bookingId, out status!);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<ItemGroup>
<ProjectReference Include="..\CoworkingBooking.BookingService.Application\CoworkingBooking.BookingService.Application.csproj" />
<ProjectReference Include="..\CoworkingBooking.BookingService.Infrastructure\CoworkingBooking.BookingService.Infrastructure.csproj" />
<ProjectReference Include="..\CoworkingBooking.CoreLib\CoworkingBooking.CoreLib.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.10" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>

<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="MassTransit" Version="8.2.0" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
</ItemGroup>

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using MassTransit;
using CoworkingBooking.CoreLib.Messaging;

namespace BookingService.Api.Orchestrator;

public class ReserveRoomActivity : IActivity<ReserveRoomArguments, ReserveRoomLog>
{
public async Task<ExecutionResult> Execute(ExecuteContext<ReserveRoomArguments> context)
{
// имитация проверок и резервирования
await Task.Delay(50);
return context.Completed(new ReserveRoomLog(context.Arguments.RoomId));
}

public async Task<CompensationResult> Compensate(CompensateContext<ReserveRoomLog> context)
{
// откат резервирования
await Task.Delay(10);
return context.Compensated();
}
}
Loading