You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Jan 27, 2022. It is now read-only.
Copy file name to clipboardExpand all lines: README.md
+213-7Lines changed: 213 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,7 +9,7 @@ A facade library useful for [Entity](https://github.com/wintoncode/Winton.Domain
9
9
10
10
This implementations allow multiple types to be transparently stored in one collection using 'wrapper' documents with type discriminators and namespaced IDs (for entities). It can be tempting for those from a traditional SQL background to provision a separate collection per type. However, this is often unnecessarily expensive, especially if much of the reserved throughput for a given collection is unused. Taking advantage of the "schemaless" nature of a document store, such as DocumentDb, can both reduce cost and simplify infrastructural complexity. This implementation provides an easy way to work with a single collection within a bounded context (within which persisted type names are unique) while outwardly still achieving the desired level of strong typing. There really is a schema, but the database doesn't need to know about it.
11
11
12
-
## Facade Types
12
+
## Facade Interfaces
13
13
14
14
Note that the default implementations are currently **incompatible with partitioned collections**. This restriction could potentially be lifted in a future version, at the expense of implementation complexity (and probably a leakier abstraction). However, for applications requiring large collections, where partitioning is actually needed, the conveniences provided by this facade are unlikely to be suitable anyway.
15
15
@@ -21,7 +21,7 @@ An abstraction layer over [Entity](https://github.com/wintoncode/Winton.DomainMo
21
21
22
22
An abstraction layer over Value Object operations in DocumentDb. Provides strong typed Create, Delete, and Query methods.
23
23
24
-
## Usage
24
+
## Domain Object Persistence
25
25
26
26
The default implementations of both `IEntityFacade` and `IValueObjectFacade` should be created from their provided factories. These can each be constructed from an [IDocumentClient](https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.documents.idocumentclient). Their Create methods both take a [Database](https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.documents.database) and a [DocumentCollection](https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.documents.documentcollection). Of course, resolving all dependencies from a DI container is preferred, but for clarity they can be manually constructed as
27
27
@@ -34,8 +34,9 @@ DocumentCollection documentCollection = new DocumentCollection { Id = "Accountin
Consider some application with an "Accounting" domain containing 2 entity types, `Account` and `Transaction`, each with a [strong typed](https://tech.winton.com/2017/06/strong-typing-a-pattern-for-more-robust-code/) ID. Note that both ID types use [SingleValueConverter](https://github.com/wintoncode/Winton.Extensions.Serialization.Json#singlevalueconverter), so they are string-serializable. The "Accounting" domain also contains a value object type, `AccountType`, used as reference data.
@@ -98,7 +99,6 @@ public struct AccountType : IEquatable<AccountType>
98
99
stringname,
99
100
decimalrate,
100
101
...)
101
-
: base(id)
102
102
{
103
103
Name=name;
104
104
Rate=rate;
@@ -144,7 +144,7 @@ public interface IAccountTypeRepository
144
144
}
145
145
```
146
146
147
-
The respective implementations of these repositories, potentially defined in a separate "Persistence" layer, would simply be thin wrappers around the `IEntityFacade` or `IValueObjectFacade`.
147
+
The respective implementations of these repositories, potentially defined in a separate persistence layer, would simply be thin wrappers around the `IEntityFacade` or `IValueObjectFacade`.
@@ -214,4 +214,210 @@ internal sealed class AccountTypeRepository : IAccountTypeRepository
214
214
}
215
215
```
216
216
217
-
These repositories will store their respective types in a single shared collection, using the type names to discriminate between each type and namespace the IDs (for entities). Therefore, these names should be considered part of the document schema, and would require a data migration if they were changed as part of a domain refactoring.
217
+
These repositories will store their respective types in a single shared collection, using the type names to discriminate between each type and namespace the IDs (for entities). Therefore, these names should be considered part of the document schema, and would require a data migration if they were changed as part of a domain refactoring.
218
+
219
+
## Data Transfer Object Persistence
220
+
221
+
In most applications, it is desirable to completely separate persistence concerns from domain logic. The above examples slightly violate this principle by leaking some Json serialization details into the domain objects. However, this can be easily avoided, at the expense of having to define some Data Transfer Objects (DTOs), and mapping functions. Consider essentially the same domain objects, but with all serialization logic removed.
The persistence layer DTOs, including serialization logic, for these simple types would then look very similar to the original domain objects.
267
+
268
+
```csharp
269
+
internalstructAccountDto
270
+
{
271
+
[JsonConstructor]
272
+
publicAccountDto(
273
+
stringid,
274
+
AccountTypeDtotype,
275
+
...)
276
+
{
277
+
Type=type;
278
+
...
279
+
}
280
+
281
+
publicstringId { get; }
282
+
283
+
publicAccountTypeDtoType { get; }
284
+
285
+
...
286
+
}
287
+
288
+
internalstructTransactionDto
289
+
{
290
+
[JsonConstructor]
291
+
publicTransactionDto(
292
+
stringid,
293
+
stringsender,
294
+
stringrecipient,
295
+
...)
296
+
{
297
+
Sender=sender;
298
+
Recipient=recipient;
299
+
...
300
+
}
301
+
302
+
publicstringId { get; }
303
+
304
+
publicstringSender { get; }
305
+
306
+
publicstringRecipient { get; }
307
+
308
+
...
309
+
}
310
+
311
+
internalstructAccountTypeDto
312
+
{
313
+
[JsonConstructor]
314
+
publicAccountTypeDto(
315
+
stringname,
316
+
decimalrate,
317
+
...)
318
+
{
319
+
Name=name;
320
+
Rate=rate;
321
+
...
322
+
}
323
+
324
+
publicstringName { get; }
325
+
326
+
publicdecimalRate { get; }
327
+
328
+
...
329
+
}
330
+
```
331
+
332
+
The overloaded implementations of both `IEntityFacade` and `IValueObjectFacade` can be created from the same factories, but by specifying the appropriate DTO and mapping function. They can be manually constructed as
Of course, this method results in more code overall, due to the need to define the DTOs, and lambdas to perform the mappings. However, these types and conversions are very simple, and full separation of domain and persistence concerns has been achieved. As before, these repositories will store their respective types in a single shared collection, using the **domain object** type names to discriminate between each type.
0 commit comments