Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "DOCUMENTATION: Update v2.10.3 FromTask" #296

Closed
Closed
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
42 changes: 11 additions & 31 deletions 1. Brokers/1. Brokers.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,17 +213,18 @@ Here's a real-life implementation of an entire storage broker for all CRUD opera
In order to abstract at an operational level all example implementations in this section will utilize the ValueTask type to provide an asynchronization abstraction.
This approach encourages a consistent handling of asynchronous operations across all method implementations, promoting a clean and efficient abstraction that aligns with Standard principles.

### For IStorageBroker.cs:
##### For IStorageBroker.cs:
```csharp
namespace OtripleS.Web.Api.Brokers.Storages
{
public partial interface IStorageBroker
{
}
}

```

### For StorageBroker.cs:
##### For StorageBroker.cs:
```csharp
using System;
using System.Linq;
Expand Down Expand Up @@ -253,7 +254,8 @@ namespace OtripleS.Web.Api.Brokers.Storages
return @object;
}

private ValueTask<IQueryable<T>> SelectAllAsync<T>() where T : class => this.Set<T>();
private ValueTask<IQueryable<T>> SelectAllAsync<T>() where T : class =>
await ValueTask.FromResult(this.Set<T>());

private async ValueTask<T> SelectAsync<T>(params object[] @objectIds) where T : class =>
await this.FindAsync<T>(objectIds);
Expand Down Expand Up @@ -349,7 +351,7 @@ This responsibility lies in the hands of the broker-neighboring services. I also
## 1.7 FAQs
Over time, some common questions arose from the engineers I worked with throughout my career. Since some of these questions reoccurred on several occasions, it might be helpful to aggregate them here so everyone can learn about other perspectives on brokers.

### 1.7.0 Is the Brokers Pattern the same as the Repository Pattern?
#### 1.7.0 Is the Brokers Pattern the same as the Repository Pattern?
From an operational standpoint, brokers seem to be more generic than repositories.

Repositories usually target storage-like operations, mainly towards databases. However, brokers can be an integration point for any external dependency, such as e-mail services, queues, and other APIs.
Expand All @@ -360,56 +362,34 @@ In general, all these patterns try to implement the same SOLID principles: separ

But because SOLID are principles and not exact guidelines, it's expected to see all different implementations and patterns to achieve that principle.

### 1.7.1 Why can't the brokers implement a contract for methods that return an interface instead of a concrete model?
#### 1.7.1 Why can't the brokers implement a contract for methods that return an interface instead of a concrete model?
That would be an ideal situation, but that would also require brokers to do a conversion or mapping between the native models returned from the external resource SDKs or APIs and the internal model that adheres to the local contract.

Doing that on the broker level will require introducing business logic into that realm, which is outside the purpose of that component.

We define business logic code as any intended sequential, selective, or iteration code. Brokers are not unit-tested because they lack business logic. They may be part of an acceptance or integration test but certainly not part of unit-level tests simply because they do not contain business logic.

### 1.7.2 If brokers were a layer of abstraction from the business logic, why do we allow external exceptions to leak through them onto the services layer?
#### 1.7.2 If brokers were a layer of abstraction from the business logic, why do we allow external exceptions to leak through them onto the services layer?
Brokers are only the *first* layer of abstraction, but not the only one. Broker-neighboring services are responsible for converting the native exceptions occurring in a broker into a more local exception model that can be handled and processed internally within the business logic realm.

Business logic emerges in the processing, orchestration, coordination, and aggregation layers, where all the exceptions, returned models, and operations are local to the system.

### 1.7.3 Why do we use partial classes for brokers who handle multiple entities?
#### 1.7.3 Why do we use partial classes for brokers who handle multiple entities?
Since brokers must own their configurations, it makes more sense to partialize when possible to avoid reconfiguring every storage broker for each entity.

Partial classes are a feature in the C# language, but it should be possible to implement the same behavior through inheritance in other programming languages.

### 1.7.4 Are brokers the same as providers (Provider Pattern)?
#### 1.7.4 Are brokers the same as providers (Provider Pattern)?
No. Providers blur the line between services (business logic) and brokers (integration layer). Brokers target particular disposable components within the system, but providers include more than just that.

### 1.7.5 Why is ValueTask preferred over Task in the implementations outlined in this section?
#### 1.7.5 Why is ValueTask preferred over Task in the implementations outlined in this section?
ValueTask is a struct that is used to represent a task that returns a value.
It is a value-based representation of a task that can be used to avoid allocations in the case where a task completes synchronously.

In order to abstract at an operational level, all example implementations in this section utilize the ValueTask type to provide an asynchronization abstraction.
This approach encourages a consistent handling of asynchronous operations across all method implementations, promoting a clean and efficient abstraction that aligns with the Standard principles.
By using ValueTask, we reduce memory allocations in scenarios where the result is often available immediately, thereby enhancing performance while maintaining code clarity.

### 1.7.6 Why does or how can I supress warnings thrown in my code files?
Warnings are thrown by the compiler to alert you of potential issues in your code. It is important to address these warnings to ensure that your code is clean and free of potential bugs.
Engineers and developers can resolve warnings by following the suggestions provided by the compiler. This may involve making changes to the code, refactoring, or updating dependencies.

It is important to regularly review and address warnings in your code to maintain code quality and ensure that your application runs smoothly.

Here is one way to resolve warnings in your code:
To suppress a warning given by the IDE or compiler, you can configure your .csproj file using the <NoWarn> element.
This approach allows you to disable specific warnings globally across your project.

For example, adding <NoWarn>CS1998</NoWarn> to a <PropertyGroup> in your .csproj file will suppress the warning CS1998, which indicates an asynchronous method lacking await operators. If you need to suppress multiple warnings, you can list them separated by commas. This method provides a clean and consistent way to manage warnings without scattering #pragma warning directives throughout your code.
```csharp
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>disable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<WarningLevel>CS1998</WarningLevel>
</PropertyGroup>
...
```

[*] [Implementing Abstract Components (Brokers)](https://www.youtube.com/watch?v=6NlgSskQXSo)

Expand Down
10 changes: 5 additions & 5 deletions 2. Services/2.1 Foundations/2.1 Foundations.md
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ private async void ShouldThrowValidationExceptionOnAddWhenIdIsInvalidAndLogItAsy

StudentValidationException actualStudentValidationException =
await Assert.ThrowsAsync<StudentValidationException>(
addStudentTask.AsTask);
registerStudentTask.AsTask);

// then
actualStudentValidationException.Should().BeEquivalentTo(
Expand Down Expand Up @@ -759,12 +759,12 @@ private async Task ShouldThrowValidationExceptionOnAddIfUpdatedByNotSameAsCreate
innerException: invalidStudentException);

// when
ValueTask<Student> addStudentTask =
ValueTask<Student> registerStudentTask =
this.studentService.AddStudentAsync(inputStudent);

StudentValidationException actualStudentValidationException =
await Assert.ThrowsAsync<StudentValidationException>(
addStudentTask.AsTask);
registerStudentTask.AsTask);

// then
actualStudentValidationException.Should().BeEquivalentTo(
Expand Down Expand Up @@ -1042,12 +1042,12 @@ private async void ShouldThrowDependencyValidationExceptionOnAddIfStudentAlready
.ThrowsAsync(duplicateKeyException);

// when
ValueTask<Student> addStudentTask =
ValueTask<Student> registerStudentTask =
this.studentService.AddStudentAsync(inputStudent);

StudentDependencyValidationException actualStudentDependencyValidationException =
await Assert.ThrowsAsync<StudentDependencyValidationException>(
addStudentTask.AsTask);
registerStudentTask.AsTask);

// then
actualStudentDependencyValidationException.Should().BeEquivalentTo(
Expand Down
Loading