Skip to content

Commit 55bb92d

Browse files
committed
evolution
1 parent fd62ba8 commit 55bb92d

File tree

15 files changed

+621
-81
lines changed

15 files changed

+621
-81
lines changed

RoyalCode.EnterprisePatterns/Directory.Build.props

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
<Project>
22
<PropertyGroup>
3-
<LibTargets>netstandard2.1;net8</LibTargets>
3+
<LibTargets>net8</LibTargets>
44
<AspTargets>net8</AspTargets>
55
<MsgTargets>net8</MsgTargets>
66
</PropertyGroup>
77
<PropertyGroup>
88

99
<DomainVer>1.0.0</DomainVer>
10-
<DomainPreview>-preview-6.0</DomainPreview>
10+
<DomainPreview>-preview-7.0</DomainPreview>
1111

1212
<PersistVer>1.0.0</PersistVer>
13-
<PersistPreview>-preview-6.0</PersistPreview>
13+
<PersistPreview>-preview-7.0</PersistPreview>
1414

1515
<CommandVer>0.1.0</CommandVer>
1616
<CommandPreview>-preview-1</CommandPreview>

RoyalCode.EnterprisePatterns/RoyalCode.Outbox.Api/OutboxApi.cs

+16-6
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,34 @@ public static class OutboxApi
1717
/// <summary>
1818
/// Adds/Maps the endpoints for reading the Outbox via HTTP.
1919
/// </summary>
20-
/// <param name="group"></param>
21-
public static void MapOutbox(this RouteGroupBuilder group)
20+
/// <param name="app">Application endpoint router.</param>
21+
/// <param name="groupPrefix">Optional, group prefix, default is 'outbox'.</param>
22+
public static void MapOutbox(this IEndpointRouteBuilder app, string groupPrefix = "outbox")
2223
{
23-
var outboxGroup = group.MapGroup("outbox")
24+
var outboxGroup = app.MapGroup(groupPrefix)
2425
.WithName("outbox")
2526
.WithDescription("Outbox API");
2627

27-
outboxGroup.MapPost("consumer", RegisterConsumerAsync)
28+
outboxGroup.MapOutbox();
29+
}
30+
31+
/// <summary>
32+
/// Adds/Maps the endpoints for reading the Outbox via HTTP.
33+
/// </summary>
34+
/// <param name="group">The RouteGroup.</param>
35+
public static void MapOutbox(this RouteGroupBuilder group)
36+
{
37+
group.MapPost("consumer", RegisterConsumerAsync)
2838
.WithName("register-consumer")
2939
.WithDescription("Register a new outbox consumer")
3040
.WithOpenApi();
3141

32-
outboxGroup.MapGet("consumer/{consumer}/messages", GetConsumerMessagesAsync)
42+
group.MapGet("consumer/{consumer}/messages", GetConsumerMessagesAsync)
3343
.WithName("get-outbox-messages")
3444
.WithDescription("get the outbox next messages for the consumer")
3545
.WithOpenApi();
3646

37-
outboxGroup.MapPost("consumer/{consumer}/commit", CommitConsumedAsync)
47+
group.MapPost("consumer/{consumer}/commit", CommitConsumedAsync)
3848
.WithName("commit-consumed-outbox-messages")
3949
.WithDescription("Commit the consumed messages")
4050
.WithOpenApi();

RoyalCode.EnterprisePatterns/RoyalCode.Repositories.Abstractions/DataServices.cs

+69-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
using RoyalCode.Entities;
3+
using RoyalCode.SmartValidations.Entities;
34
using System.Diagnostics.CodeAnalysis;
45

56
namespace RoyalCode.Repositories.Abstractions;
@@ -17,6 +18,7 @@ namespace RoyalCode.Repositories.Abstractions;
1718
/// </remarks>
1819
/// <typeparam name="TEntity">The entity type.</typeparam>
1920
public interface IAdder<in TEntity>
21+
where TEntity : class
2022
{
2123
/// <summary>
2224
/// <para>
@@ -33,6 +35,22 @@ public interface IAdder<in TEntity>
3335
/// <param name="entity">The new entity instance.</param>
3436
void Add(TEntity entity);
3537

38+
/// <summary>
39+
/// <para>
40+
/// Adds a new entity to the repository to be persisted.
41+
/// </para>
42+
/// </summary>
43+
/// <remarks>
44+
/// <para>
45+
/// When implemented together with the Unit Of Work pattern the entity
46+
/// will not be persisted directly when calling this method,
47+
/// it will be stored in memory until the completion of the unit of work.
48+
/// </para>
49+
/// </remarks>
50+
/// <param name="entity">The new entity instance.</param>
51+
/// <param name="token">Cancellation token.</param>
52+
ValueTask AddAsync(TEntity entity, CancellationToken token = default);
53+
3654
/// <summary>
3755
/// <para>
3856
/// Adds a collection of new entities to the repository to be persisted.
@@ -56,6 +74,7 @@ public interface IAdder<in TEntity>
5674
/// </summary>
5775
/// <typeparam name="TEntity">The entity type.</typeparam>
5876
public interface IFinder<TEntity>
77+
where TEntity : class
5978
{
6079
/// <summary>
6180
/// <para>
@@ -83,6 +102,21 @@ public interface IFinder<TEntity>
83102
/// </para>
84103
/// </returns>
85104
ValueTask<TEntity?> FindAsync(object id, CancellationToken token = default);
105+
106+
/// <summary>
107+
/// <para>
108+
/// Try to find an existing entity through its unique identity (Id).
109+
/// </para>
110+
/// </summary>
111+
/// <typeparam name="TId">The type o entity id.</typeparam>
112+
/// <param name="id">The entity id.</param>
113+
/// <param name="token">Cancellation token.</param>
114+
/// <returns>
115+
/// <para>
116+
/// An entry representing the entity record obtained from the database.
117+
/// </para>
118+
/// </returns>
119+
ValueTask<Entry<TEntity, TId>> FindAsync<TId>(Id<TEntity, TId> id, CancellationToken token = default);
86120
}
87121

88122
/// <summary>
@@ -99,7 +133,7 @@ public interface IFinder<TEntity>
99133
/// </remarks>
100134
/// <typeparam name="TEntity">The entity type.</typeparam>
101135
public interface IFinderByGuid<TEntity>
102-
where TEntity : IHasGuid
136+
where TEntity : class, IHasGuid
103137
{
104138
/// <summary>
105139
/// <para>
@@ -145,7 +179,7 @@ public interface IFinderByGuid<TEntity>
145179
/// <typeparam name="TEntity">The entity type.</typeparam>
146180
/// <typeparam name="TCode">The code type.</typeparam>
147181
public interface IFinderByCode<TEntity, in TCode>
148-
where TEntity : IHasCode<TCode>
182+
where TEntity : class, IHasCode<TCode>
149183
{
150184
/// <summary>
151185
/// <para>
@@ -190,6 +224,7 @@ public interface IFinderByCode<TEntity, in TCode>
190224
[SuppressMessage("Major Code Smell", "S2326:Unused type parameters should be removed",
191225
Justification = "Update an entity, used to identify the entity")]
192226
public interface IUpdater<TEntity>
227+
where TEntity : class
193228
{
194229
/// <summary>
195230
/// <para>
@@ -272,6 +307,37 @@ IEnumerable<bool> MergeRange<TId>(IEnumerable<IHasId<TId>> models)
272307
/// </returns>
273308
Task<bool> MergeAsync<TId>(IHasId<TId> model, CancellationToken token = default);
274309

310+
/// <summary>
311+
/// <para>
312+
/// Operation to merge a data model to an existing entity.
313+
/// </para>
314+
/// </summary>
315+
/// <remarks>
316+
/// <para>
317+
/// The data model should have an id, which will be used to get the entity from the database.
318+
/// </para>
319+
/// <para>
320+
/// The fields of the data model should be the same as the entity's fields.
321+
/// </para>
322+
/// <para>
323+
/// When implemented together with the Unit Of Work pattern the entity
324+
/// will not be persisted directly when calling this method,
325+
/// it will be stored in memory until the completion of the unit of work.
326+
/// </para>
327+
/// </remarks>
328+
/// <typeparam name="TId">The Id type.</typeparam>
329+
/// <typeparam name="TModel">The model with the data.</typeparam>
330+
/// <param name="id">The id value.</param>
331+
/// <param name="model">The model.</param>
332+
/// <param name="token">Cancellation token.</param>
333+
/// <returns>
334+
/// <para>
335+
/// True if the entity exists and has been updated, false otherwise.
336+
/// </para>
337+
/// </returns>
338+
Task<bool> MergeAsync<TId, TModel>(Id<TEntity, TId> id, TModel model, CancellationToken token = default)
339+
where TModel : class;
340+
275341
/// <summary>
276342
/// <para>
277343
/// Operation to merge a data model to an existing entity.
@@ -315,6 +381,7 @@ async Task<IEnumerable<bool>> MergeRangeAsync<TId>(
315381
/// </remarks>
316382
/// <typeparam name="TEntity">Tipo da entidade.</typeparam>
317383
public interface IRemover<TEntity>
384+
where TEntity : class
318385
{
319386
/// <summary>
320387
/// <para>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System.ComponentModel;
2+
using System.Runtime.CompilerServices;
3+
4+
namespace RoyalCode.Repositories.Abstractions;
5+
6+
/// <summary>
7+
/// Type to define the identity of an entity.
8+
/// </summary>
9+
/// <typeparam name="TEntity">Entity type.</typeparam>
10+
/// <typeparam name="TId">Type of identity value.</typeparam>
11+
public readonly struct Id<TEntity, TId>
12+
where TEntity : class
13+
{
14+
/// <summary>
15+
/// Implicit operator to convert the identity value to the identity type.
16+
/// </summary>
17+
/// <param name="id"></param>
18+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
19+
public static implicit operator Id<TEntity, TId>(TId id) => new(id);
20+
21+
/// <summary>
22+
/// Creates new value for the organisation's identity.
23+
/// </summary>
24+
/// <param name="value"></param>
25+
public Id(TId value) => Value = value;
26+
27+
/// <summary>
28+
/// The value of the entity's identity.
29+
/// </summary>
30+
public TId Value { get; }
31+
32+
/// <summary>
33+
/// String parse for the entity's identity type.
34+
/// </summary>
35+
/// <param name="input">Input, in string format.</param>
36+
/// <param name="id">Output, in the entity's identity type.</param>
37+
/// <returns>True if the parse was possible, false otherwise.</returns>
38+
public static bool TryParse(string? input, out Id<TEntity, TId> id)
39+
{
40+
if (input == null)
41+
{
42+
id = default;
43+
return true;
44+
}
45+
46+
try
47+
{
48+
var converter = TypeDescriptor.GetConverter(typeof(TId));
49+
if (converter is not null && converter.CanConvertFrom(typeof(string)))
50+
{
51+
id = new Id<TEntity, TId>((TId)converter.ConvertFromString(input)!);
52+
return true;
53+
}
54+
}
55+
catch { /* Ignore exceptions, return false at the end. */ }
56+
57+
id = default;
58+
return false;
59+
}
60+
}

RoyalCode.EnterprisePatterns/RoyalCode.Repositories.Abstractions/R.Designer.cs

+90
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)