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
15 changes: 10 additions & 5 deletions src/SomeCompany/Inventory/Inventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@
using EventStore.Client;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Npgsql;
using Transacto;
using Transacto.Framework;
using Transacto.Framework.CommandHandling;
using Transacto.Infrastructure;

namespace SomeCompany.Inventory {
public class Inventory : IPlugin {
public string Name { get; } = nameof(Inventory);

public void Configure(IEndpointRouteBuilder builder)
=> builder.UseInventory();
public void Configure(IEndpointRouteBuilder builder) => builder
.MapCommands(string.Empty, typeof(DefineInventoryItem));

public void ConfigureServices(IServiceCollection services)
=> services.AddNpgSqlProjection<InventoryLedger>();
=> services
.AddSingleton<CommandHandlerModule>(provider => new InventoryItemModule(
provider.GetRequiredService<EventStoreClient>(),
provider.GetRequiredService<IMessageTypeMapper>(),
TransactoSerializerOptions.Events))
.AddNpgSqlProjection<InventoryLedgerProjection>();

public IEnumerable<Type> MessageTypes { get { yield return typeof(InventoryItemDefined); } }
}
Expand Down
5 changes: 4 additions & 1 deletion src/SomeCompany/Inventory/InventoryItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ public class InventoryItem : AggregateRoot {
public static readonly Func<InventoryItem> Factory = () => new InventoryItem();

public InventoryItemIdentifier Identifier { get; private set; }
public override string Id => Identifier.ToString();
public override string Id => FormatStreamName(Identifier);

public static string FormatStreamName(InventoryItemIdentifier identifier) =>
$"inventoryItem-{identifier}";

private InventoryItem() {
Register<InventoryItemDefined>(e => Identifier = new InventoryItemIdentifier(e.InventoryItemId));
Expand Down
2 changes: 1 addition & 1 deletion src/SomeCompany/Inventory/InventoryItemIdentifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace SomeCompany.Inventory {

public InventoryItemIdentifier(Guid value) {
if (value == Guid.Empty) {
throw new ArgumentException();
throw new ArgumentOutOfRangeException(nameof(value));
}

_value = value;
Expand Down
7 changes: 5 additions & 2 deletions src/SomeCompany/Inventory/InventoryItemModule.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Text.Json;
using EventStore.Client;
using Transacto.Framework;
using Transacto.Framework.CommandHandling;

namespace SomeCompany.Inventory {
public class InventoryItemModule : CommandHandlerModule {
Expand All @@ -9,12 +10,14 @@ public InventoryItemModule(EventStoreClient eventStore,
Build<DefineInventoryItem>()
.Log()
.UnitOfWork(eventStore, messageTypeMapper, serializerOptions)
.Handle((_, ct) => {
.Handle(async (_, ct) => {
var (unitOfWork, command) = _;
var handlers = new InventoryItemHandlers(
new InventoryItemRepository(eventStore, messageTypeMapper, unitOfWork));

return handlers.Handle(command, ct);
await handlers.Handle(command, ct);

return Position.Start;
});
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/SomeCompany/Inventory/InventoryItemRepository.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using EventStore.Client;
using Transacto.Framework;
using Transacto.Framework.CommandHandling;
using Transacto.Infrastructure;

namespace SomeCompany.Inventory {
Expand All @@ -8,9 +9,8 @@ public class InventoryItemRepository {

public InventoryItemRepository(EventStoreClient eventStore,
IMessageTypeMapper messageTypeMapper, UnitOfWork unitOfWork) {
_inner = new EventStoreRepository<InventoryItem>(eventStore, unitOfWork,
InventoryItem.Factory, id => $"inventoryItem-{id}", messageTypeMapper,
TransactoSerializerOptions.Events);
_inner = new EventStoreRepository<InventoryItem>(eventStore, unitOfWork, InventoryItem.Factory,
messageTypeMapper, TransactoSerializerOptions.Events);
}

public void Add(InventoryItem inventoryItem) => _inner.Add(inventoryItem);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
using Transacto;

namespace SomeCompany.Inventory {
public class InventoryLedger : NpgsqlProjection {
public InventoryLedger() : base(new Scripts()) {
public class InventoryLedgerProjection : NpgsqlProjection {
public InventoryLedgerProjection() : base(new Scripts()) {
When<CreateSchema>();

When<InventoryItemDefined>(e => new[] {
Expand Down
7 changes: 0 additions & 7 deletions src/SomeCompany/Inventory/InventoryMiddleware.cs

This file was deleted.

1 change: 0 additions & 1 deletion src/SomeCompany/Inventory/Scripts.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using SomeCompany.Infrastructure;
using Transacto;

namespace SomeCompany.Inventory {
Expand Down
40 changes: 19 additions & 21 deletions src/SomeCompany/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Dapper;
using EventStore.Client;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -14,12 +17,13 @@
namespace SomeCompany {
internal class Program : IDisposable {
private readonly CancellationTokenSource _exitedSource;
private readonly IStreamStore _streamStore;
private readonly IHostBuilder _hostBuilder;

private Program(SomeCompanyConfiguration configuration) {
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
DefaultTypeMap.MatchNamesWithUnderscores = true;
Inflector.Inflector.SetDefaultCultureFunc = () => new CultureInfo("en-US");
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
//.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Console(
Expand All @@ -29,24 +33,19 @@ private Program(SomeCompanyConfiguration configuration) {

_exitedSource = new CancellationTokenSource();

var connectionStringBuilder = new NpgsqlConnectionStringBuilder(configuration.ConnectionString);

_streamStore = new InMemoryStreamStore();

_hostBuilder = Host.CreateDefaultBuilder()
.ConfigureLogging(builder => builder.AddSerilog())
.ConfigureWebHost(builder => builder
.UseKestrel()
.ConfigureServices(services => services
.AddEventStoreClient()
.AddSingleton(connectionStringBuilder)
.AddSingleton(_streamStore))
.UseStartup(new Startup(GetPlugins())));

static IPlugin[] GetPlugins() =>
typeof(Startup).Assembly.GetExportedTypes().Where(typeof(IPlugin).IsAssignableFrom)
.Select(t => (IPlugin)Activator.CreateInstance(t)!)
.ToArray();
_hostBuilder = TransactoHost.Build(new ServiceCollection()
.AddEventStoreClient(settings => settings.CreateHttpMessageHandler = () => new SocketsHttpHandler {
SslOptions = {
RemoteCertificateValidationCallback = delegate { return true; }
}
})
.AddSingleton<IStreamStore>(new HttpClientSqlStreamStore(new HttpClientSqlStreamStoreSettings {
BaseAddress = new UriBuilder {
Port = 5002
}.Uri
}))
.AddSingleton(new NpgsqlConnectionStringBuilder(configuration.ConnectionString))
.BuildServiceProvider());
}

private async Task<int> Run() {
Expand All @@ -69,7 +68,6 @@ public static async Task<int> Main(string[] args) {

public void Dispose() {
_exitedSource.Dispose();
_streamStore.Dispose();
}
}
}
5 changes: 5 additions & 0 deletions src/SomeCompany/PurchaseOrders/PurchaseOrder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using EventStore.Client;
using Transacto.Domain;

namespace SomeCompany.PurchaseOrders {
Expand Down Expand Up @@ -37,5 +39,8 @@ public IEnumerable<object> GetAdditionalChanges() {
}

public int? Version { get; set; }

[JsonIgnore]
public long Position { get; set; }
}
}
20 changes: 13 additions & 7 deletions src/SomeCompany/PurchaseOrders/PurchaseOrderMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using EventStore.Client;
using Hallo;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Transacto;
using Transacto.Framework;

namespace SomeCompany.PurchaseOrders {
public static class PurchaseOrderMiddleware {
Expand All @@ -16,7 +18,8 @@ public static void UsePurchaseOrders(this IEndpointRouteBuilder builder,
.MapGet(string.Empty, async context => {
var orders = await purchaseOrders.List(context.RequestAborted);

return new HalResponse(PurchaseOrderListRepresentation.Instance, orders);
return new HalResponse(context.Request, PurchaseOrderListRepresentation.Instance,
ETag.Create(orders.Max(x => x.Position)), new Optional<object>(orders));
})
.MapPost(string.Empty, async (HttpContext context, PurchaseOrder purchaseOrder) => {
if (purchaseOrder.PurchaseOrderId == Guid.Empty) {
Expand All @@ -25,29 +28,31 @@ public static void UsePurchaseOrders(this IEndpointRouteBuilder builder,

await purchaseOrders.Save(purchaseOrder, context.RequestAborted);

return new HalResponse(PurchaseOrderRepresentation.Instance, purchaseOrder) {
return new HalResponse(context.Request, PurchaseOrderRepresentation.Instance,
ETag.Create(purchaseOrder.Version), purchaseOrder) {
StatusCode = HttpStatusCode.Created,
Headers = {
("location", purchaseOrder.PurchaseOrderId.ToString())
Location = new Uri(purchaseOrder.PurchaseOrderId.ToString())
}
};
})
.MapGet("{purchaseOrderId:guid}", async context => {
if (!context.TryParseGuid(nameof(PurchaseOrder.PurchaseOrderId), out var purchaseOrderId)) {
return new HalResponse(PurchaseOrderRepresentation.Instance) {
return new HalResponse(context.Request, PurchaseOrderRepresentation.Instance) {
StatusCode = HttpStatusCode.NotFound
};
}

var order = await purchaseOrders.Get(purchaseOrderId, context.RequestAborted);

return new HalResponse(PurchaseOrderRepresentation.Instance, order) {
return new HalResponse(context.Request, PurchaseOrderRepresentation.Instance,
ETag.Create(order.HasValue ? order.Value.Position : new long?()), order) {
StatusCode = order.HasValue ? HttpStatusCode.OK : HttpStatusCode.NotFound
};
})
.MapPut("{purchaseOrderId:guid}", async (HttpContext context, PurchaseOrder purchaseOrder) => {
if (!context.TryParseGuid(nameof(purchaseOrder.PurchaseOrderId), out var purchaseOrderId)) {
return new HalResponse(PurchaseOrderRepresentation.Instance) {
return new HalResponse(context.Request, PurchaseOrderRepresentation.Instance) {
StatusCode = HttpStatusCode.NotFound
};
}
Expand All @@ -56,7 +61,8 @@ public static void UsePurchaseOrders(this IEndpointRouteBuilder builder,

await purchaseOrders.Save(purchaseOrder, context.RequestAborted);

return new HalResponse(PurchaseOrderRepresentation.Instance, purchaseOrder);
return new HalResponse(context.Request, PurchaseOrderRepresentation.Instance,
ETag.Create(purchaseOrder.Version), purchaseOrder);
})
.MapBusinessTransaction<PurchaseOrder>("{purchaseOrderId:guid}");
}
Expand Down
4 changes: 2 additions & 2 deletions src/SomeCompany/PurchaseOrders/PurchaseOrderRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ namespace SomeCompany.PurchaseOrders {
public class PurchaseOrderRepository {
private readonly string _schema;
private readonly Func<CancellationToken, Task<NpgsqlConnection>> _connectionFactory;
private readonly SqlStreamStoreBusinessTransactionRepository<PurchaseOrder> _inner;
private readonly StreamStoreBusinessTransactionRepository<PurchaseOrder> _inner;

public PurchaseOrderRepository(IStreamStore streamStore, string schema,
Func<CancellationToken, Task<NpgsqlConnection>> connectionFactory) {
_schema = schema;
_connectionFactory = connectionFactory;
_inner = new SqlStreamStoreBusinessTransactionRepository<PurchaseOrder>(streamStore,
_inner = new StreamStoreBusinessTransactionRepository<PurchaseOrder>(streamStore,
order => GetStreamName(order.PurchaseOrderId), new JsonSerializerOptions());
}

Expand Down
4 changes: 2 additions & 2 deletions src/SomeCompany/ReceiptOfGoods/ReceiptOfGoods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using Transacto.Domain;
using Transacto.Framework;

namespace SomeCompany.ReceiptOfGoods {
partial class ReceiptOfGoods : IBusinessTransaction {
Expand All @@ -11,7 +10,8 @@ private static (Credit, Debit) Accumulate((Credit, Debit) _, ReceiptOfGoodsItem
return (inventoryInTransit + item.Total, inventoryOnHand + item.Total);
}

public GeneralLedgerEntryNumber ReferenceNumber => new GeneralLedgerEntryNumber("goodsReceipt-" + ReceiptOfGoodsNumber);
public GeneralLedgerEntryNumber ReferenceNumber =>
new GeneralLedgerEntryNumber("goodsReceipt", ReceiptOfGoodsNumber);

public void Apply(GeneralLedgerEntry entry, ChartOfAccounts chartOfAccounts) {
var (inventoryInTransit, inventoryOnHand) = ReceiptOfGoodsItems.Aggregate(
Expand Down
2 changes: 1 addition & 1 deletion src/SomeCompany/ReceiptOfGoods/ReceiptOfGoods.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"title": "Purchase Order Id",
"$ref": "#/definitions/uuid",
"x-schema-form": {
"key": "purchaseOrderId",
"key": "purchaseOrderId",
"type": "uuid"
}
},
Expand Down
2 changes: 2 additions & 0 deletions src/SomeCompany/SomeCompany.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
<RestoreSources>$(RestoreSources);https://api.nuget.org/v3/index.json;https://nuget.pkg.github.com/thefringeninja/index.json</RestoreSources>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<LangVersion>8.0</LangVersion>
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SqlStreamStore.Http" Version="1.2.0-beta.*" />
<PackageReference Include="Transacto.Tasks" Version="0.0.0-alpha.*" />
</ItemGroup>

Expand Down
6 changes: 3 additions & 3 deletions src/SomeCompany/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6181,9 +6181,9 @@ normalize-url@^1.4.0:
sort-keys "^1.0.0"

notevil@^1.1.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/notevil/-/notevil-1.3.2.tgz#8b35c39ad02b080eea6cb217e5ad3d34ac615a26"
integrity sha512-V8b+4l/BPnV5TRt2mV4xk4xGDWjbWtcFc1+TSM3arR9AhxBEVz0/gGFjHepQbUASi9g7N0ltWYoKz3M0w+HSfA==
version "1.3.3"
resolved "https://registry.yarnpkg.com/notevil/-/notevil-1.3.3.tgz#56b8a935d8978e0c000749621aca3928b823cb01"
integrity sha512-y4gR18Z2lIHeBREaZu788iii4/KLLe2jNPoZA8aEg4NWK1JwjmtjVyI3eypQKPEkOIPc++8C+byUUDc+SJDJgg==
dependencies:
esprima "~1.0"
hoister "~0.0"
Expand Down
Loading