LazyGuard
LazyGuard
CC#
Created by LazyGuard on 12/13/2024 in #help
Which Architecture to Choose?
I am working with two .NET-based architectures for processing HTTP POST requests, and I’m looking to wondering what to choose and how to measure things. Here's how the two architectures work: Architecture 1: Decoupled Worker with Kafka A POST request writes data to a database and sends a message to a Kafka topic. A separate worker app consumes messages from Kafka, processes them, and updates the database. Architecture 2: Background Service in the Same App A POST request writes data to a database. A background service running in the same application continuously polls the database for unprocessed tasks and processes them one by one. Architecture 1 decouples the request handling and background processing using Kafka, allowing independent scaling of the worker. Architecture 2 couples the request handling and background processing within the same app. In what conditions I must chose the first or second one? Performance-wise how should I go about testing both ? Thanks
52 replies
CC#
Created by LazyGuard on 7/31/2024 in #help
✅ Which API design is better
I have orders that can be put in an issue status and then resolve that issue There is two options: 1. The first one is having two different endpoints PATCH /orders/{orderId}/create-issue with the following body
{
"issueDate": ......
"reason": ......
}
{
"issueDate": ......
"reason": ......
}
and PATCH /orders/{orderId}/resolve with the following body
{
"resolveDate": ......
"solution": ......
}
{
"resolveDate": ......
"solution": ......
}
2. The second one is having a single endpoint PATCH /orders/{orderId}/issue and we either pass an issueDate + reason OR resolveDate + solution plus a validation if we pass inconsistent data (for example passing a body that contains a reason + solution will result in a validation error since we can not create an issue and resolve it at the same time Question: Which option is better and why?
2 replies
CC#
Created by LazyGuard on 4/20/2024 in #help
How to know if it's worth it to implement a circuit breaker ?
No description
28 replies
CC#
Created by LazyGuard on 3/21/2024 in #help
✅ Functional programming instead of exceptions
I've watched this video where Derek implements an Either from scratch to make method signatures more explicit rather than throwing an exception. He also adds a Map method to force the client code to deal with both cases However, the problem is that not every method has one successful path and one failure path. For example, the PostAsync. has many possible ways to fail (see exceptions here https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.postasync?view=net-8.0 ). So how to deal with this kind of situations ? especially if we want to keep the granularity of all the different ways to fail and we want to handle different failures in different ways in the client code
35 replies
CC#
Created by LazyGuard on 3/19/2024 in #help
Architecture Review
No description
7 replies
CC#
Created by LazyGuard on 3/19/2024 in #help
How to handle long running requests ?
No description
25 replies
CC#
Created by LazyGuard on 3/17/2024 in #help
A question about aggregates in DDD
I am watching a DDD course and the author created the following classes
public record AggregateId
{
private Guid Value { get; }

public AggregateId() : this(Guid.NewGuid())
{
}

private AggregateId(Guid value)
{
if (value == Guid.Empty)
{
throw new InvalidAggregateIdException(value);
}

Value = value;
}

public static implicit operator Guid(AggregateId id) => id.Value;
public static implicit operator AggregateId(Guid id) => new(id);

public override string ToString()
{
return Value.ToString();
}
}

public abstract class AggregateRoot
{
public AggregateId Id { get; protected set; }

// TIP: it's a good practice to include the version in the aggregate root.
// It's useful for optimistic concurrency control.
public int Version { get; protected set; }


//The order of the events matter, so we use a list.
private readonly List<IDomainEvent> _events = [];
public IEnumerable<IDomainEvent> Events => _events;


protected void AddEvent(IDomainEvent @event)
{
// The version is incremented only if there are no events in the list.
// If we changed 4 properties in out aggregate, we want to avoid change version from 1 to 5
if (_events.Count == 0)
{
Version++;
}
_events.Add(@event);
}

public void ClearEvents() => _events.Clear();
}
public record AggregateId
{
private Guid Value { get; }

public AggregateId() : this(Guid.NewGuid())
{
}

private AggregateId(Guid value)
{
if (value == Guid.Empty)
{
throw new InvalidAggregateIdException(value);
}

Value = value;
}

public static implicit operator Guid(AggregateId id) => id.Value;
public static implicit operator AggregateId(Guid id) => new(id);

public override string ToString()
{
return Value.ToString();
}
}

public abstract class AggregateRoot
{
public AggregateId Id { get; protected set; }

// TIP: it's a good practice to include the version in the aggregate root.
// It's useful for optimistic concurrency control.
public int Version { get; protected set; }


//The order of the events matter, so we use a list.
private readonly List<IDomainEvent> _events = [];
public IEnumerable<IDomainEvent> Events => _events;


protected void AddEvent(IDomainEvent @event)
{
// The version is incremented only if there are no events in the list.
// If we changed 4 properties in out aggregate, we want to avoid change version from 1 to 5
if (_events.Count == 0)
{
Version++;
}
_events.Add(@event);
}

public void ClearEvents() => _events.Clear();
}
24 replies
CC#
Created by LazyGuard on 3/16/2024 in #help
✅ How to modernize this code ?
So, I have found this .Net Core 3.1 code and want to make it more modern.
using System.Collections;
using System.Runtime.CompilerServices;
using Chocco.Services.Availability.Core.Exceptions;
using Chocco.Services.Availability.Core.ValueObjects;

namespace Chocco.Services.Availability.Core.Entities;

public class Resource : AggregateRoot
{
private ISet<string> _tags = new HashSet<string>();
private ISet<Reservation> _reservations = new HashSet<Reservation>();

public IEnumerable<string> Tags
{
get => _tags;
private set => _tags = new HashSet<string>(value);
}

public IEnumerable<Reservation> Reservations
{
get => _reservations;
private set => _reservations = new HashSet<Reservation>(value);
}

public Resource(AggregateId id, IEnumerable<string> tags, IEnumerable<Reservation>? reservations = null, int version = 0)
{
var enumerable = tags.ToList();

ValidateTags(enumerable);
Id = id;
Tags = enumerable;
Reservations = reservations ?? Enumerable.Empty<Reservation>();
Version = version;
}

private static void ValidateTags(IEnumerable<string> tags)
{
var enumerable = tags.ToList();
if (tags is null || !enumerable.Any())
{
throw new MissingResourceTagsException();
}

if (enumerable.Any(string.IsNullOrWhiteSpace))
{
throw new InvalidResourceTagsException();
}
}
}
using System.Collections;
using System.Runtime.CompilerServices;
using Chocco.Services.Availability.Core.Exceptions;
using Chocco.Services.Availability.Core.ValueObjects;

namespace Chocco.Services.Availability.Core.Entities;

public class Resource : AggregateRoot
{
private ISet<string> _tags = new HashSet<string>();
private ISet<Reservation> _reservations = new HashSet<Reservation>();

public IEnumerable<string> Tags
{
get => _tags;
private set => _tags = new HashSet<string>(value);
}

public IEnumerable<Reservation> Reservations
{
get => _reservations;
private set => _reservations = new HashSet<Reservation>(value);
}

public Resource(AggregateId id, IEnumerable<string> tags, IEnumerable<Reservation>? reservations = null, int version = 0)
{
var enumerable = tags.ToList();

ValidateTags(enumerable);
Id = id;
Tags = enumerable;
Reservations = reservations ?? Enumerable.Empty<Reservation>();
Version = version;
}

private static void ValidateTags(IEnumerable<string> tags)
{
var enumerable = tags.ToList();
if (tags is null || !enumerable.Any())
{
throw new MissingResourceTagsException();
}

if (enumerable.Any(string.IsNullOrWhiteSpace))
{
throw new InvalidResourceTagsException();
}
}
}
PArticulary, what I don't like is that converting tags into List. IF I remove it the compiler warns me about multiple possible enumeration (what the heck is this !) Another thing is there a way to keep exposing an IEnumerable but having Sets as implementation? Any other suggestions are also welcome as well
60 replies
CC#
Created by LazyGuard on 3/10/2024 in #help
How to cope with this downside of MediatR?
This is a quote from Derek Comartin that describes well the issue. "One of the biggest downside to in-process processing (which MediatR does), is it does not isolate the actual handling of a request. This isn’t a problem with MediatR at all, just the nature of it executing in-process. Everything is done in-process. Meaning that wherever process is calling mediator.Send() is also the same process that is executing the relevant Handler for that request. A notification can have zero or multiple handlers. However the execution of all the notification handlers are done all in the same process. If you publish a notification to MediatR, and there are 3 handlers for that notification, it will execute all 3. But what happens if one of them fails and throws an exception? That exception will bubble up back to where you published the message via MediatR. How do you want to handle this? Can you re-publish that message again and have the handlers that succeeded run again? What if one of them was sending an email ?" Now my question is, other than moving to out-of-process messaging, how to cope with this disadvantage ?
5 replies
CC#
Created by LazyGuard on 2/9/2024 in #help
null!
I am receiving some infos from an API and I have to decerialize to a model I am doing somthing like
if (response.IsSuccess)
//deserialize to Person
else
// deserialize to ProblemDetails
if (response.IsSuccess)
//deserialize to Person
else
// deserialize to ProblemDetails
And in person I hava defined properties using public sting Name {get; set;} = null! Is the use of null! here a bad pratice ?
53 replies
CC#
Created by LazyGuard on 12/17/2023 in #help
Any suggestions for an Architecture improvements ?
No description
232 replies
CC#
Created by LazyGuard on 11/29/2023 in #help
How to test this code?
I am trying to refactor some code in a background service that looks like this:
public class MyBackgroundService : BackgroundService {
public MyBackgroundService(IKafkaConsumer consumer, IOptionMonithor<WorkerConfig> config, NotificationSender sender)
{
_consumer = consumer;
_config = config;
_sender = sender;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while(!stoppingToken.IsCancellationRequested)
{
await ConsumeKafkaAndSendNotificationAsync();
await Task.Delay(TimeSpan.FromMilliseconds(_config.CurrentValue.Delay), stoppingToken);
}
}

private async Task ConsumeKafkaAndSendNotificationAsyncc()
{
var timer = new StopWatch();
timer.Start();
Message? message;
do
{
message = _consumer.ConsumeMessage();
// .....
// do some work here on the message
// .....
await _sender.SendNotificationAsync(message);
} while (
message != null && timer.Elapsed < TimeSpan.FromMilliseconds(_config.CurrentValue.Delay)
)
}
}
public class MyBackgroundService : BackgroundService {
public MyBackgroundService(IKafkaConsumer consumer, IOptionMonithor<WorkerConfig> config, NotificationSender sender)
{
_consumer = consumer;
_config = config;
_sender = sender;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while(!stoppingToken.IsCancellationRequested)
{
await ConsumeKafkaAndSendNotificationAsync();
await Task.Delay(TimeSpan.FromMilliseconds(_config.CurrentValue.Delay), stoppingToken);
}
}

private async Task ConsumeKafkaAndSendNotificationAsyncc()
{
var timer = new StopWatch();
timer.Start();
Message? message;
do
{
message = _consumer.ConsumeMessage();
// .....
// do some work here on the message
// .....
await _sender.SendNotificationAsync(message);
} while (
message != null && timer.Elapsed < TimeSpan.FromMilliseconds(_config.CurrentValue.Delay)
)
}
}
Complex work should usually be kept somewhere else outside of the loop of the backgroundService. However, things get hairy enough that some tests will help.
5 replies
CC#
Created by LazyGuard on 10/24/2023 in #help
❔ Refactoring strategy pattern to a simpler code
I have the following code and I want te refactor it to a less-boiler-plate code. Wha'ts suposed to do is that the Handle method should take a product as input and 1.decides to which output system it needs to send information (depending on some configuration and on the product itself). There could be more thant 1 output systems to send the product to. 2. convert the Product to the appropriate format for chosen output systems.
3. send to those systems
public class XMLHandler
{
private readonly IOutputSystemStrategyFactory _strategyFactory;
private readonly IOutputSystemDecider _outputSystemsDecider;

public XMLHandler(IOutputSystemStrategyFactory strategyFactory, IOutputSystemDecider outputSystemsDecider)
{
_strategyFactory = strategyFactory;
_outputSystemsDecider = outputSystemsDecider;
}

public void Handle(Product product)
{
IEnumerable<OutputSystem> outputSystems = _outputSystemsDecider.GetOutputSystems(product);

foreach (OutputSystem outputSystem in outputSystems)
{
IOutputSystemStrategy strategy = _strategyFactory.Create(outputSystem);
strategy.GenerateAndSend(product);
}
}
}
public class XMLHandler
{
private readonly IOutputSystemStrategyFactory _strategyFactory;
private readonly IOutputSystemDecider _outputSystemsDecider;

public XMLHandler(IOutputSystemStrategyFactory strategyFactory, IOutputSystemDecider outputSystemsDecider)
{
_strategyFactory = strategyFactory;
_outputSystemsDecider = outputSystemsDecider;
}

public void Handle(Product product)
{
IEnumerable<OutputSystem> outputSystems = _outputSystemsDecider.GetOutputSystems(product);

foreach (OutputSystem outputSystem in outputSystems)
{
IOutputSystemStrategy strategy = _strategyFactory.Create(outputSystem);
strategy.GenerateAndSend(product);
}
}
}
14 replies
CC#
Created by LazyGuard on 8/27/2023 in #help
✅ Nulls in C#
113 replies
CC#
Created by LazyGuard on 6/22/2023 in #help
❔ Prevent accepting numeric values as enum
Hello, I have an API that exposes an enum field Vehicle (Car, Bike, Truck) in the POST. The problem is that when I POST a request with a body {"vehicle" :"1" }, it gets accepted and gets automatically converted to Vehicle.Bike.. But something like {"vehicle" :"bar" } results in a bad request, which is what I need ! Any idea on how to stop parsing integer like enum values?
31 replies
CC#
Created by LazyGuard on 6/19/2023 in #help
✅ How to parse this weird API ?
I have a very akward thing to do and I d'ont know if there is a clean way to do it. In a REST API, there is two string fields attributeName and attributeValue (for example { "attributeName" : "isAllowed" , " attributeValue" : "true"} and { "attributeName" : "productType" , " attributeValue" : "A50"} , in the API this is modelled by the following class N.B: the "attribute" here have nothing to do with any .NET vocabulary, it's just a name, think of it like "foo" or "bar"
public record ApiCommand
{
public string? AttributeName {get; init;}
public string? AttributeValue {get; init;}
}
public record ApiCommand
{
public string? AttributeName {get; init;}
public string? AttributeValue {get; init;}
}

In my Domain I have a class with all possible properties
public record Porduct
{
public bool? IsAllowed {get; init;}
public ProductType? ProductType {get; init;} // ProductType is an enum
// There are other properties as well
}
public record Porduct
{
public bool? IsAllowed {get; init;}
public ProductType? ProductType {get; init;} // ProductType is an enum
// There are other properties as well
}

what I need to do is, depending on the AttributeName and AttributeValue given in the API, I want to create an instance of Product which has all everything equal to null except for the property for which we were given the name and value in AttributeName and AttributeValue. (there is always a single one) For exemple { "attributeName" : "isAllowed" , " attributeValue" : "true"} would result in a product Instance with product.IsAllowed = true and product.ProductType = null (and other fields will be null as well) Another example, { "attributeName" : "productType" , " attributeValue" : "A50"} would result in product Instance with product.IsAllowed = null and product.ProductType = A50 (and other fields will be null as well)
122 replies
CC#
Created by LazyGuard on 6/8/2023 in #help
❔ Where to put this logic ?
Hi folks, I have a very simple project that have 3 layers : Api, Domain and Infrastructure I have a mongo collection which contain users. I want to implement a very simple feature. Delete a user by its Id. I have two options : 1. In the domain I first try to get a user by the given Id. If it's there I delete it, if not I throw a NotFoundException. This exception is then catched in the API layer to return a Not Found Status code. The disadvantage of this approach is that I am doing 2 access to the database. 2. I simply try to delete the user in the infrastructure, then I use the WriteResult of mongo. And there are here two sub-options: i. Either In the infrastructure, I check that the nbRemoved is not 0. If it is the case, the infrastructure throw a NotFoundExceptionthat will be catched in the API and mapped to Not Found status code. In this case, I think the disadvantage is that the domain does not contain much, it's a logic that should be in the domain but that's put in the infrastructure. ii Or the infrastructure will just return nbRemoved (and not the whole WriteResult) and thus the domain checks if it's 0 and throw the exception I am trying to choose between 1. and 2.i and 2.ii. Any help ?
36 replies
CC#
Created by LazyGuard on 5/28/2023 in #help
❔ How to design this code ?
Hi folks, I am having some troubles coming up with a design for the following problem. I have a Product object that have a several properties (e.g. category (a string), dangerous (a boolean), type (enum), length(int), height(int), weight(int) etc.) There is a set of rule that allows to set the value of some properties based on conditions on other properties. For example : Set the Type property to "Big" when Length >2 and Weight >15. Another example : "Set the Dangerous to true when Category == fireworks. Those rule must not be harcoded in the code, because our users want to have the possibility to add such rules on their own. Does anyone have an idea on how to do this ? Some of my pain point are : - How to model a rule in a nosql database ? - How to apply it seamlessly on the Product objects etc.
38 replies
CC#
Created by LazyGuard on 12/27/2022 in #help
❔ How to deserialize correctly
Hi, I have the following records :
public record ProductInformation
{
public ProductDetails Product {get; init;} // Warnings: Non-nullable property Product is unitialized & auto-property accessor is never user


public record ProductDetails
{
public Product {get; init;} // Warnings: Non-nullable property Product is unitialized & auto-property accessor is never user
public init OwnerId {get; init;} //auto-property accessor is never user
}
public record ProductInformation
{
public ProductDetails Product {get; init;} // Warnings: Non-nullable property Product is unitialized & auto-property accessor is never user


public record ProductDetails
{
public Product {get; init;} // Warnings: Non-nullable property Product is unitialized & auto-property accessor is never user
public init OwnerId {get; init;} //auto-property accessor is never user
}
this is a little bit weird (the name Product is used twice), because unfortunately the records are used to deserialize objects returned from an external API that returns a list either empty or have the following format:
[
{
"product": {
"ownerId": "39",
"product": "1_4873jm_jd"
}
]
[
{
"product": {
"ownerId": "39",
"product": "1_4873jm_jd"
}
]
To deserialize, I am using
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (string.IsNullOrEmpty(responseString))
return null;
var products = JsonSerializer.Deserialize<IReadOnlyCollection<ProductInformation>>(responseString)
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (string.IsNullOrEmpty(responseString))
return null;
var products = JsonSerializer.Deserialize<IReadOnlyCollection<ProductInformation>>(responseString)
The problem is that I don't know how to do this properly in order not to have the warnings. Any idea ?
11 replies
CC#
Created by LazyGuard on 11/25/2022 in #help
❔ Is there a way to improve this code Performance ?
have a collection of items that have _id and serialNumbers (an array) fields (and other fields as well but irrelevant for the question). I want to do the following: 0. Check that the newSerialNumber is not used in any document. 1. Filter to find out the item with the given id, lets name it ItemX 2. Get all the items that have the same serialNumbers as ItemX. Let's name them ItemsWithSameSerialNumbersAsItemX 3. For each one of the ItemsWithSameSerialNumbersAsItemX, push the newSerialNumber I am doing it as following:
var itemsWithNewSerialNumber = items.find( x => x.SerialNumbers == newSerialNumber );
if (itemsWithNewSerialNumber.Count > 0) {
// throw exception
}

var itemX = items.findOne( x => x.ItemId == Id );

var itemsWithSameSerialNumbersAsItemX = items.find( x => x.SerialNumbers == itemX.SerialNumbers);

var updateDef = Builders<ItemsDto>.Update;

var updates = new List<UpdateDefinition<ItemDto>>
{
updateDef.Push(x => x.SerialNumbers, newSerialNumber)
}
items.updateMany(
x => x.SerialNumbers,
updates }
)
var itemsWithNewSerialNumber = items.find( x => x.SerialNumbers == newSerialNumber );
if (itemsWithNewSerialNumber.Count > 0) {
// throw exception
}

var itemX = items.findOne( x => x.ItemId == Id );

var itemsWithSameSerialNumbersAsItemX = items.find( x => x.SerialNumbers == itemX.SerialNumbers);

var updateDef = Builders<ItemsDto>.Update;

var updates = new List<UpdateDefinition<ItemDto>>
{
updateDef.Push(x => x.SerialNumbers, newSerialNumber)
}
items.updateMany(
x => x.SerialNumbers,
updates }
)
Here I am making 4 calls to the database (3 finds and 1 update). Is there some way to write this more efficiently ?
5 replies