Skip to content

Commit 4fdba04

Browse files
authored
Merge pull request #3 from wintoncode/result-types
Add result type.
2 parents 6841677 + 1be5751 commit 4fdba04

20 files changed

+1045
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
bin/
44
obj/
55
.vs/
6+
.vscode/
67
*.suo
78
*.user

Winton.DomainModelling.Abstractions.sln.DotSettings

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,12 @@
3434
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
3535
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
3636
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/OLD_ENGINE/@EntryValue">True</s:Boolean>
37+
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
3738
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_CONSTRUCTOR_INITIALIZER_ON_SAME_LINE/@EntryValue">False</s:Boolean>
3839
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
40+
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
3941
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
42+
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_EMBEDDED_STATEMENT_ON_SAME_LINE/@EntryValue">NEVER</s:String>
4043
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_WHILE_ON_NEW_LINE/@EntryValue">True</s:Boolean>
4144
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_EMBEDDED_STATEMENT_STYLE/@EntryValue">LINE_BREAK</s:String>
4245
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
@@ -456,6 +459,11 @@
456459
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
457460
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
458461
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
462+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
463+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
464+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
465+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
466+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
459467

460468

461469

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) Winton. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
7+
namespace Winton.DomainModelling
8+
{
9+
/// <summary>
10+
/// Extension methods for asynchronous results which make it possible to chain async results together
11+
/// using a fluent API in the same way as synchronous results.
12+
/// </summary>
13+
public static class AsyncResultExtensions
14+
{
15+
/// <summary>
16+
/// Invokes another result generating function which takes as input the data of this result
17+
/// if it is successful after it has been awaited.
18+
/// </summary>
19+
/// <remarks>
20+
/// If this result is a failure then this is a no-op and the original failure is retained.
21+
/// This is useful for chaining serial operations together that return results.
22+
/// </remarks>
23+
/// <typeparam name="TData">
24+
/// The type of data encapsulated by the result.
25+
/// </typeparam>
26+
/// <typeparam name="TNewData">
27+
/// The type of data in the new result.
28+
/// </typeparam>
29+
/// <param name="resultTask">
30+
/// The async result that this extension method is invoked on.
31+
/// </param>
32+
/// <param name="onSuccess">
33+
/// The function that is invoked if this result represents a success.
34+
/// </param>
35+
/// <returns>
36+
/// If this result is a success, then the result of <paramref>onSuccess</paramref> function;
37+
/// otherwise the original error.
38+
/// </returns>
39+
public static async Task<Result<TNewData>> Then<TData, TNewData>(
40+
this Task<Result<TData>> resultTask,
41+
Func<TData, Result<TNewData>> onSuccess)
42+
{
43+
Result<TData> result = await resultTask;
44+
return result.Then(onSuccess);
45+
}
46+
47+
/// <summary>
48+
/// Invokes another result generating function which takes as input the data of this result
49+
/// if it is successful after it has been awaited.
50+
/// </summary>
51+
/// <remarks>
52+
/// If this result is a failure then this is a no-op and the original failure is retained.
53+
/// This is useful for chaining serial operations together that return results.
54+
/// </remarks>
55+
/// <typeparam name="TData">
56+
/// The type of data encapsulated by the result.
57+
/// </typeparam>
58+
/// <typeparam name="TNewData">
59+
/// The type of data in the new result.
60+
/// </typeparam>
61+
/// <param name="resultTask">
62+
/// The async result that this extension method is invoked on.
63+
/// </param>
64+
/// <param name="onSuccess">
65+
/// The asynchronous function that is invoked if this result represents a success.
66+
/// </param>
67+
/// <returns>
68+
/// If this result is a success, then the result of <paramref>onSuccess</paramref> function;
69+
/// otherwise the original error.
70+
/// </returns>
71+
public static async Task<Result<TNewData>> Then<TData, TNewData>(
72+
this Task<Result<TData>> resultTask,
73+
Func<TData, Task<Result<TNewData>>> onSuccess)
74+
{
75+
Result<TData> result = await resultTask;
76+
return await result.Then(onSuccess);
77+
}
78+
}
79+
}

src/Winton.DomainModelling.Abstractions/DomainException.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Winton.DomainModelling
99
/// <summary>
1010
/// Represents domain errors.
1111
/// </summary>
12+
[Obsolete("Prefer to return results with an Error instead.", false)]
1213
public class DomainException : Exception
1314
{
1415
/// <summary>

src/Winton.DomainModelling.Abstractions/EntityNotFoundException.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Winton.DomainModelling
99
/// <summary>
1010
/// An error indicating that an entity could not be found.
1111
/// </summary>
12+
[Obsolete("Prefer to return results with a NotFoundError instead.", false)]
1213
public class EntityNotFoundException : DomainException
1314
{
1415
private EntityNotFoundException(string message)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) Winton. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace Winton.DomainModelling
5+
{
6+
/// <summary>
7+
/// The base class of all errors in the domain model.
8+
/// </summary>
9+
/// <remarks>
10+
/// In a domain model it is better to explicitly model errors rather than throw exceptions.
11+
/// Errors are legitimate outcomes of operations in a domain model where an operation is
12+
/// not guaranteed to succeed. Errors can occur for several reasons, such as not being able
13+
/// to find a requested entity or because the inputs received were invalid.
14+
/// On the other hand, exceptions should be used in exceptional circumstances that have
15+
/// occurred due to factors outside of the domain model's control, e.g. network failures.
16+
/// In general, problems in the domain model should be modelled as errors and exceptions should
17+
/// be reserved for cases where the user cannot be given information on how to correct the problem.
18+
/// </remarks>
19+
public class Error
20+
{
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="Error" /> class.
23+
/// </summary>
24+
/// <param name="detail">The detail that describes the error.</param>
25+
/// <returns>A new instance of <see cref="Error" />.</returns>
26+
public Error(string detail)
27+
{
28+
Detail = detail;
29+
}
30+
31+
/// <summary>
32+
/// Gets the detail that describes the error.
33+
/// </summary>
34+
public string Detail { get; }
35+
}
36+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright (c) Winton. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
7+
namespace Winton.DomainModelling
8+
{
9+
/// <inheritdoc />
10+
/// <summary>
11+
/// A result indicating a failure.
12+
/// </summary>
13+
public sealed class Failure<TData> : Result<TData>
14+
{
15+
/// <summary>
16+
/// Initializes a new instance of the <see cref="Failure{TData}" /> class.
17+
/// </summary>
18+
/// <param name="error">The error that caused the result to be a failure.</param>
19+
/// <returns>A new instance of <see cref="Failure{TData}" />.</returns>
20+
public Failure(Error error)
21+
{
22+
Error = error;
23+
}
24+
25+
/// <summary>
26+
/// Gets the error that caused the failure.
27+
/// </summary>
28+
public Error Error { get; }
29+
30+
/// <inheritdoc />
31+
public override Result<TNewData> Combine<TOtherData, TNewData>(
32+
Result<TOtherData> other,
33+
Func<TData, TOtherData, TNewData> combineData,
34+
Func<Error, Error, Error> combineErrors)
35+
{
36+
return other.Match<Result<TNewData>>(
37+
otherData => new Failure<TNewData>(Error),
38+
otherError => new Failure<TNewData>(combineErrors(Error, otherError)));
39+
}
40+
41+
/// <inheritdoc />
42+
public override T Match<T>(Func<TData, T> onSuccess, Func<Error, T> onFailure)
43+
{
44+
return onFailure(Error);
45+
}
46+
47+
/// <inheritdoc />
48+
public override Result<TNewData> Select<TNewData>(Func<TData, TNewData> selectData)
49+
{
50+
return new Failure<TNewData>(Error);
51+
}
52+
53+
/// <inheritdoc />
54+
public override Task<Result<TNewData>> Select<TNewData>(Func<TData, Task<TNewData>> selectData)
55+
{
56+
return Task.FromResult<Result<TNewData>>(new Failure<TNewData>(Error));
57+
}
58+
59+
/// <inheritdoc />
60+
public override Result<TNextData> Then<TNextData>(Func<TData, Result<TNextData>> onSuccess)
61+
{
62+
return new Failure<TNextData>(Error);
63+
}
64+
65+
/// <inheritdoc />
66+
public override Task<Result<TNextData>> Then<TNextData>(Func<TData, Task<Result<TNextData>>> onSuccess)
67+
{
68+
return Task.FromResult<Result<TNextData>>(new Failure<TNextData>(Error));
69+
}
70+
}
71+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) Winton. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace Winton.DomainModelling
5+
{
6+
/// <inheritdoc />
7+
/// <summary>
8+
/// An error indicating that an entity could not be found.
9+
/// </summary>
10+
public class NotFoundError : Error
11+
{
12+
/// <summary>
13+
/// Initializes a new instance of the <see cref="NotFoundError" /> class.
14+
/// </summary>
15+
/// <param name="detail">The detail that describes the error.</param>
16+
/// <returns>A new instance of <see cref="NotFoundError" />.</returns>
17+
public NotFoundError(string detail)
18+
: base(detail)
19+
{
20+
}
21+
}
22+
}

0 commit comments

Comments
 (0)