Domain Driven Design | Invoking external services to enrich entity properties - c#

For study purposes I have designed a product-service which allows to perform CRUD operations against a product entity made up by the following properties:
string ID
string Name
decimal ListPrice
decimal FinalPrice
the ID, the Name and the ListPrice are stored in the product-service's database, while the FinalPrice is retrieved by an external service, the price-card-service, which exposes the following endpoint: GET /price-card-by-id/{product-id} which returns an object containing the product's final price.
The Create and Update operations of the product-service don't allow to do anything with the product's FinalPrice, but when retrieving a Product it will have its FinalPrice if returned by the price-card-service
By now the logic of retrieving and setting the FinalPrice is handled withing the GetProductByIdRequestHandler:
public class GetProductByIdRequestHandler : IAppRequestHandler<GetProductByIdRequest, GetProductByIdResponse>
{
private readonly IProductRepository _productRepository;
private readonly PriceCardServiceClient _priceCardServiceClient;
public GetProductByIdRequestHandler(IProductRepository productRepository, PriceCardServiceClient priceCardServiceClient)
{
_productRepository = productRepository;
_priceCardServiceClient = priceCardServiceClient;
}
public async Task<OneOf<GetProductByIdResponse, IError>> Handle(GetProductByIdRequest request, CancellationToken cancellationToken)
{
var product = await _productRepository.GetById(request.ProductId);
if (product == null)
{
return new NotFoundError
{
Message = $"Could not find product {request.ProductId}"
};
}
var priceCardList = await _priceCardServiceClient.ActiveAsync(product.Id, cancellationToken);
if (!priceCardList.Items.Any())
{
product.FinalPrice = product.Price;
return new GetProductByIdResponse(product);
}
var priceCard = priceCardList.Items.First();
if (priceCard.NewPrice < 0)
{
return new PriceCardNewPriceLessThanZeroError
{
Message = $"Price {priceCard.NewPrice} for PriceCard {priceCard.Id} for Product {product.Id} must be greater or equal to 0"
};
}
product.FinalPrice = ProductPrice.From(System.Convert.ToDecimal(priceCard.NewPrice));
return new GetProductByIdResponse(product);
}
}
I'm wondering if DDD promotes a different way for handling this kind of operations, perhaps by injecting the PriceCardServiceClient into the Product entity and creating a readonly FinalPrice property which handles the logic.
What are the possible approches?

It depends from your domain, mainly.
Is the Product, as a domain object, requiring this information? Then you could:
store it directly, requiring it before via the service,
obtain it every time you need the entity.
The first solution requires a 'update' process, that at least for each valid Product, aligns the stored value with the current valid one. The second one gives you the current value every time, but it also makes that part of the domain dependent from another service. In other words, you could not get the Product if the service PriceCardServiceClient is not responding. Or, you should manage this special case, once again with extra code.
If, instead, your domain entity doesn't require it, if you load/show/manage it is just related to the UI. Hence, using the DDD will not cause any change in the way you get this information.

Related

Block Controller Method while already running

I have a controller which returns a large json object. If this object does not exist, it will generate and return it afterwards. The generation takes about 5 seconds, and if the client sent the request multiple times, the object gets generated with x-times the children. So my question is: Is there a way to block the second request, until the first one finished, independent who sent the request?
Normally I would do it with a Singleton, but because I am having scoped services, singleton does not work here
Warning: this is very oppinionated and maybe not suitable for Stack Overflow, but here it is anyway
Although I'll provide no code... when things take a while to generate, you don't usually spend that time directly in controller code, but do something like "start a background task to generate the result, and provide a "task id", which can be queried on another different call).
So, my preferred course of action for this would be having two different controller actions:
Generate, which creates the background job, assigns it some id, and returns the id
GetResult, to which you pass the task id, and returns either different error codes for "job id doesn't exist", "job id isn't finished", or a 200 with the result.
This way, your clients will need to call both, however, in Generate, you can check if the job is already being created and return an existing job id.
This of course moves the need to "retry and check" to your client: in exchange, you don't leave the connection to the server opened during those 5 seconds (which could potentially be multiplied by a number of clients) and return fast.
Otherwise, if you don't care about having your clients wait for a response during those 5 seconds, you could do a simple:
if(resultDoesntExist) {
resultDoesntExist = false; // You can use locks for the boolean setters or Interlocked instead of just setting a member
resultIsBeingGenerated = true;
generateResult(); // <-- this is what takes 5 seconds
resultIsBeingGenerated = false;
}
while(resultIsBeingGenerated) { await Task.Delay(10); } // <-- other clients will wait here
var result = getResult(); // <-- this should be fast once the result is already created
return result;
note: those booleans and the actual loop could be on the controller, or on the service, or wherever you see fit: just be wary of making them thread-safe in however method you see appropriate
So you basically make other clients wait till the first one generates the result, with "almost" no CPU load on the server... however with a connection open and a thread from the threadpool used, so I just DO NOT recommend this :-)
PS: #Leaky solution above is also good, but it also shifts the responsability to retry to the client, and if you are going to do that, I'd probably go directly with a "background job id", instead of having the first (the one that generates the result) one take 5 seconds. IMO, if it can be avoided, no API action should ever take 5 seconds to return :-)
Do you have an example for Interlocked.CompareExchange?
Sure. I'm definitely not the most knowledgeable person when it comes to multi-threading stuff, but this is quite simple (as you might know, Interlocked has no support for bool, so it's customary to represent it with an integral type):
public class QueryStatus
{
private static int _flag;
// Returns false if the query has already started.
public bool TrySetStarted()
=> Interlocked.CompareExchange(ref _flag, 1, 0) == 0;
public void SetFinished()
=> Interlocked.Exchange(ref _flag, 0);
}
I think it's the safest if you use it like this, with a 'Try' method, which tries to set the value and tells you if it was already set, in an atomic way.
Besides simply adding this (I mean just the field and the methods) to your existing component, you can also use it as a separate component, injected from the IOC container as scoped. Or even injected as a singleton, and then you don't have to use a static field.
Storing state like this should be good for as long as the application is running, but if the hosted application is recycled due to inactivity, it's obviously lost. Though, that won't happen while a request is still processing, and definitely won't happen in 5 seconds.
(And if you wanted to synchronize between app service instances, you could 'quickly' save a flag to the database, in a transaction with proper isolation level set. Or use e.g. Azure Redis Cache.)
Example solution
As Kit noted, rightly so, I didn't provide a full solution above.
So, a crude implementation could go like this:
public class SomeQueryService : ISomeQueryService
{
private static int _hasStartedFlag;
private static bool TrySetStarted()
=> Interlocked.CompareExchange(ref _hasStartedFlag, 1, 0) == 0;
private static void SetFinished()
=> Interlocked.Exchange(ref _hasStartedFlag, 0);
public async Task<(bool couldExecute, object result)> TryExecute()
{
if (!TrySetStarted())
return (couldExecute: false, result: null);
// Safely execute long query.
SetFinished();
return (couldExecute: true, result: result);
}
}
// In the controller, obviously
[HttpGet()]
public async Task<IActionResult> DoLongQuery([FromServices] ISomeQueryService someQueryService)
{
var (couldExecute, result) = await someQueryService.TryExecute();
if (!couldExecute)
{
return new ObjectResult(new ProblemDetails
{
Status = StatusCodes.Status503ServiceUnavailable,
Title = "Another request has already started. Try again later.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.4"
})
{ StatusCode = StatusCodes.Status503ServiceUnavailable };
}
return Ok(result);
}
Of course possibly you'd want to extract the 'blocking' logic from the controller action into somewhere else, for example an action filter. In that case the flag should also go into a separate component that could be shared between the query service and the filter.
General use action filter
I felt bad about my inelegant solution above, and I realized that this problem can be generalized into basically a connection number limiter on an endpoint.
I wrote this small action filter that can be applied to any endpoint (multiple endpoints), and it accepts the number of allowed connections:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ConcurrencyLimiterAttribute : ActionFilterAttribute
{
private readonly int _allowedConnections;
private static readonly ConcurrentDictionary<string, int> _connections = new ConcurrentDictionary<string, int>();
public ConcurrencyLimiterAttribute(int allowedConnections = 1)
=> _allowedConnections = allowedConnections;
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var key = context.HttpContext.Request.Path;
if (_connections.AddOrUpdate(key, 1, (k, v) => ++v) > _allowedConnections)
{
Close(withError: true);
return;
}
try
{
await next();
}
finally
{
Close();
}
void Close(bool withError = false)
{
if (withError)
{
context.Result = new ObjectResult(new ProblemDetails
{
Status = StatusCodes.Status503ServiceUnavailable,
Title = $"Maximum {_allowedConnections} simultaneous connections are allowed. Try again later.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.4"
})
{ StatusCode = StatusCodes.Status503ServiceUnavailable };
}
_connections.AddOrUpdate(key, 0, (k, v) => --v);
}
}
}

Dynamically creating Objects at runtime with parameters DI vs Factory

I currently have a case where I have multiple customers who have variability of on what forms of notifications they want (ie email, fax, etc.). They could also want one or more than one. So I have created a factory with some basic Reflection that will dynamically create concrete classes depending on some parameters that get sent in through the customer profile. I'm curious is there a better way to do this using ASP.Net Core Dependency Injection? I have put in the factory code here to help folks understand of what I'm trying to do.
The customer profile will send in a string array of what services they have subscribed as a parameter to CreateInstances so only specific services would be dynamically created.
public Dictionary<string, Type> Notifications;
public NotificationFactory()
{
LoadTypes();
}
public IEnumerable<INotificationService> CreateInstances(params string[] namesOfServices)
{
var servicesToInstantiate = namesOfServices.ToList();
List<INotificationService> result = new List<INotificationService>();
foreach (var service in servicesToInstantiate)
{
Type serviceName = GetServiceNameToCreate(service.ToLower());
if (serviceName != null)
{
result.Add(Activator.CreateInstance(serviceName) as INotificationService);
}
}
return result;
}
private Type GetServiceNameToCreate(string NotificationClassName)
{
return Notifications.FirstOrDefault(a => a.Key.Contains(NotificationClassName)).Value;
}
protected virtual void LoadTypes()
{
Notifications = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => typeof(INotificationService).IsAssignableFrom(t) && !t.IsInterface)
.ToDictionary(t => t.Name.ToLower(), t => t);
}
It is a bad idea to take an arbitrary string and activate an instance the way you are doing it. The string should at least be white-listed. If a malicious user were able to tamper with the strings in the database, for example, they could choose what type to activate and potentially trigger unauthorized behavior or denial of service.
Instead, define a white list of types, and associate each type with a delegate that constructs the service you need.
var map = new Dictionary<string,Func<INotificationService>>
{
{ "EMAIL" : () => new EmailService() },
{ "PHONE" : () => new PhoneService() }
};
Then to get a service, call
if (!map.ContainsKey(serviceType)) throw new ArgumentException(nameof(serviceType));
INotificationService service = map[serviceType]();
Or if you prefer:
Func<INotificationService> factory;
if (!map.TryGetValue(serviceType, out factory)) throw new ArgumentException(nameof(serviceType));
INotificationService service = factory();
This way you are certain that the service type is one that you support, and you don't need to use any Reflection.
If you need more extensibility
If you want to be able to add more delivery methods without changing the code that populates map, you can of course populate the map from a configuration file. Although I'm left wondering how you could possibly implement a new delivery method without changing any code, and whether you have a bone fide NFR that requires you to be able to. Seems to me that new delivery methods don't get added very often and would be a big deal in other respects, so there is little point in saving yourself adding one line of code here.

Branching dialogs/forms based on response in MS Bot Framework

We're experimenting with the MS Bot Framework and haven't quite worked out how to do this scenario:
We have a LUIS Dialog (type <object>), which is working correctly and is trained properly. To use the common sandwich example, the basics of what LUIS intent is looking for is the user asking for the status of an order. If the order number was provided in the question ("What is the status of order 1234?"), then the LUIS dialog does the lookup and reports the status directly (which is all currently working).
However, if the user just triggers the intent without providing the order number ("I'd like to look up the status of an order."), I'd like to launch another dialog/form to ask the user if they'd like to look up the order by address or order number, and then do the appropriate DB lookup based on how they answer.
I'm just not sure how to configure the Form/Dialog (or even which is best in this case) to do a different lookup based on if they choose address or number lookup.
Here's the intent so far:
private readonly BuildFormDelegate<OrderStatusDialog> OrderStatusDelegate;
[LuisIntent(nameof(LuisIntents.OrderStatus))]
public async Task OrderStatus(IDialogContext context, LuisResult result)
{
// Order number(s) were provided
if (result.Entities.Any(Entity => Entity.Type == nameof(LuisEntityTypes.OrderNumber)))
{
// Loop in case they asked about multiple orders
foreach (var entity in result.Entities.Where(Entity => Entity.Type == nameof(LuisEntityTypes.OrderNumber)))
{
var orderNum = entity.Entity;
// Call webservice to check status
var request = new RestRequest(Properties.Settings.Default.GetOrderByNum, Method.GET);
request.AddUrlSegment("num", orderNum);
var response = await RestHelper.SendRestRequestAsync(request);
var parsedResponse = JObject.Parse(response);
if ((bool)parsedResponse["errored"])
{
await context.PostAsync((string)parsedResponse["errMsg"]);
continue;
}
// Grab status from returned JSON
var status = parsedResponse["orderStatus"].ToString();
await context.PostAsync($"The status of order {orderNum} is {status}");
}
context.Wait(MessageReceived);
}
// Order number was not provided
else
{
var orderStatusForm = new FormDialog<OrderStatusDialog>(new OrderStatusDialog(), OrderStatusDelegate,
FormOptions.PromptInStart);
context.Call<OrderStatusDialog>(orderStatusForm, CallBack);
}
}
private async Task CallBack(IDialogContext context, IAwaitable<object> result)
{
context.Wait(MessageReceived);
}
And the form:
public enum OrderStatusLookupOptions
{
Address,
OrderNumber
}
[Serializable]
public class OrderStatusDialog
{
public OrderStatusLookupOptions? LookupOption;
public static IForm<OrderStatusDialog> BuildForm()
{
return new FormBuilder<OrderStatusDialog>()
.Message("In order to look up the status of a order, we will first need either the order number or your delivery address.")
.Build();
}
}
The FormFlow route is a valid option. What is missing in your form flow is asking for the address/order number after the lookup option is selected.
What you can do in that case is adding two more fields to the OrderStatusDialog class: OrderNumber and DeliveryAddress.
Then you need to use the selected OrderStatusLookupOptions to activate/deactivate the next field.
The code, from the top of my head, would be something like:
[Serializable]
public class OrderStatusDialog
{
public OrderStatusLookupOptions? LookupOption;
public int OrderNumber;
public string DeliveryAddress
public static IForm<OrderStatusDialog> BuildForm()
{
return new FormBuilder<OrderStatusDialog>()
.Message("In order to look up the status of a order, we will first need either the order number or your delivery address.")
.Field(nameof(OrderStatusDialog.LookupOption))
.Field(new FieldReflector<OrderStatusDialog>(nameof(OrderStatusDialog.OrderNumber))
.SetActive(state => state.LookupOption == OrderStatusLookupOptions.OrderNumber))
.Field(new FieldReflector<OrderStatusDialog>(nameof(OrderStatusDialog.DeliveryAddress))
.SetActive(state => state.LookupOption == OrderStatusLookupOptions.Address))
.Build();
}
}
Then on your Callback method you will receive the form filled and you can do the DB lookup.
Alternatively, you can just use PromptDialogs and guide the user through the same experience. Take a look to the MultiDialogs sample to see the different alternatives.
I added a working sample on this here.

Security/Validation on c# property change N-Tier

I have a class that keeps track of Property Changes
public class Property
{
object _OriginalValue;
object _ProposedValue;
DateTime _ProposedDateTime;
List<Property> _History = new List<Property>();
public object OriginalValue
{
get
{
return _OriginalValue;
}
set
{
_OriginalValue = value;
}
}
public object ProposedValue
{
get
{
return _ProposedValue;
}
set
{
_ProposedDateTime = DateTime.Now;
_ProposedValue = value;
}
}
public bool IsDirty
{
get
{
if (OriginalValue != ProposedValue)
{
return true;
}
else
{
return false;
}
}
}
}
This property can be used by classes like
public class Customer
{
protected Property _FirstName = new Property();
public string FirstName
{
get
{
return (string)_FirstName.ProposedValue;
}
set
{
_FirstName.ProposedValue = value;
}
}
public object GetOriginalValue(Property Property)
{
return Property.OriginalValue;
}
}
The question is, is there a way to secure the original value when passing this to a client in an N-Tier architecture?
When a client passes a Customer back into the Service Boundary - by default you can't trust the client. You need to either reload the original values from the database or validate that the original values are untampered. Of course I'm assuming we're going to use business logic based on the current values in the customer to reject or allow an update operation.
Example:
User inserts record with Name Bob.
User fetches record with Name Bob and changes name to Ted. Original Value is Bob, proposed Value is Ted.
User sends Customer to Service to Update Customer.
Everything is good.
*A business rule is now coded into the service that says if the customer's name is Ted - allow the update else throw "unable to update" exception. *
User fetches record with name Ted.
User changes name to Darren.
User changes name back to Ted - system throws exception.
User fetches Ted. User cheats and uses a tool to change the OriginalPropertyValue on the client.
The server doesn't refetch the OriginalValue from the database and simply reads the OriginalValue coming from the client.
User bypasses business rule.
Actually there're more issues with your approach than just checking if original value hasn't been tampered. For example, I suspect that's a multi-user environment where more than an user would be able to edit the same object. That is, the original value mightn't be tampered, but changed before other has already saved a new original value in the database.
I guess you're already applying some kind of optimistic or pessimistic locking on your data...
About your actual concern, probably you need to sign your original value, and whenever you're going to store those objects back in the database, your application layer should check that original value hasn't been tampered (from Wikipedia):
Digital signatures are a standard element of most cryptographic
protocol suites, and are commonly used for software distribution,
financial transactions, contract management software, and in other
cases where it is important to detect forgery or tampering.

Maintain state of the object for same browser session

Following is my ASP.Net Web API controller code. Here as you can see there's a private class object, BL, that is used and both the Get methods implemented. For the first method FetchAllDashboardByUserId(int userId), I pass the user id so that the BL object can be initiated. Within the same browser session, if the second get method is called, then I do not want to pass the userid, since BL should be by default initiated, but currently that's not the case. BL is null for the second method, so I have to add userid to the call to the method - GetCardDataUI(int userId, int dashBoardID, int cardID). My question is how to avoid it. Is my thinking incorrect that:
A single open browser where I make the Consecutive call to the following URLs are a single session:
webapi/ViewR?userId=1
webapi/ViewR?userId=1&dashBoardID=1&cardID=3
I don't want to pass the userId in the second URL. Please note that if I declare class object as static then it works as expected, but that's not what I want, it has to be tied to a user:
public class ViewRController : ApiController
{
// BL object for a user
private static BL accessBL = null;
// HTTP GET for Webapi/ViewR (Webapi - name of API, ViewR - Controller with implementation)
[AcceptVerbs("Get")]
public List<DashboardUI> FetchAllDashboardByUserId(int userId)
{
if (accessBL == null)
accessBL = new BL(userId);
// Use BL object for entity processing
}
[AcceptVerbs("Get")]
public CardDataGetUI GetCardDataUI(int userId, int dashBoardID, int cardID)
{
if (accessBL == null)
accessBL = new BL(userId);
// Use BL object for entity processing
}
}
How I want the second method implementation to be:
[AcceptVerbs("Get")]
public CardDataGetUI GetCardDataUI(int dashBoardID, int cardID)
{
// Use BL class object created in last call for entity processing
// Should not pass userid again
}
You can easily store data in Session:
... first request:
Session["userID"] = userID;
... next request:
int userID = (int)Session["userID"]; // should check for null first, but you get the idea...
But keep the following points in mind:
Session variables are stored as objects, so you'll need to cast and/or type-check
Session variables can be null
Session expires after a configurable (in web.config) about of time
Default session state is in-memory, meaning if the app pool is restarted session state is gone - you can store session in files or databases to keep longer
Session doesn't scale out unless you use persistent storage (file, database)
Objects stored in persistent storage must be serializable

Categories

Resources