Should Service A be dependent on service B or abstract the logic in a fat client - oop

This is a system design/architecture question around setting up 2 TIER 0 services - Service A & Service B.
Service A depends on service B for additional information to return a more accurate response. I have 2 options here:
Service A internally calls Service B for this additional information. So clients of service A use a thin client library.
Clients of service A provide this additional information in the request to Service A. Maybe by hiding the logic to call Service B before calling Service A in the client.
Features of Option 1
Client library for Service A is a thin client with no logic.
Failure and latency of Service A are tied to service B.
Sending a request to Service A is a one-stop solution for the entire request.
Features of Option 2
Client library for Service A has to have some logic to get all the request arguments from Service B before sending the request to Service A.
Failures of Service A and Service B are independent and easier to reason about.
Service A is no longer a one-stop solution for a detailed, accurate response.
Sample API for service A:
// Returns true if any of the user in the list can perform the |action| on the |resource|.
isAllowed(User[], Action, Resource): Boolean
Sample API for service B:
// Returns all the relatives for the provided list of users
getRelatives(User[]): User[]
Option 1
Now should the service A look like:
// Client for Service A:
class clientA {
isAllowed(User[] users, Action action, Resource res): Boolean = {
return ServiceA.isAllowed(users, action, res)
}
}
// Service A implementation
class ServiceA {
isAllowed(User[] users, Action action, Resource res): Boolean = {
allUsers = ServiceB.getRelatives(users) + users
return allUsers.any(user => res.canPerform(action, user))
}
Option 2
or should it look like:
// Client for Service A:
class clientA {
isAllowed(User[] users, Action action, Resource res): Boolean = {
allUsers = ServiceB.getRelatives(users) + users
return ServiceA.isAllowed(allUsers, action, res)
}
}
// Service A implementation
class ServiceA {
isAllowed(User[] users, Action action, Resource res): Boolean = {
allUsers = ServiceB.getRelatives(users) + users
return allUsers.any(user => res.canPerform(action, user))
}
}
Question:
What are the benefits of one approach over the other?
When should one pick one option over the other?

Related

Register dependent services on every request

I am working in Multi-tenant solution primarily there are 2 type of applications
WebAPI
Console app to process message from queue
I have implemented dependency injection to inject all services. I have crated TenantContext class where I am resolving tenant information from HTTP header and it's working fine for API, but console application getting tenant information with every message (tenant info is part of queue message) so I am calling dependency injection register method on every incoming message which is not correct, do you have any suggestion/solution here?
The way I am resolving ITenantContext in API
services.AddScoped<ITenantContext>(serviceProvider =>
{
//Get Tenant from JWT token
if (string.IsNullOrWhiteSpace(tenantId))
{
//1. Get HttpAccessor and processor settings
var httpContextAccessor =
serviceProvider.GetRequiredService<IHttpContextAccessor>();
//2. Get tenant information (temporary code, we will get token from JWT)
tenantId = httpContextAccessor?.HttpContext?.Request.Headers["tenant"]
.FirstOrDefault();
if (string.IsNullOrWhiteSpace(tenantId))
//throw bad request for api
throw new Exception($"Request header tenant is missing");
}
var tenantSettings =
serviceProvider.GetRequiredService<IOptionsMonitor<TenantSettings>>();
return new TenantContext(tenantId, tenantSettings );
});
Create two different ITenantContext implementations. One for your Web API, and one for your Console application.
Your Web API implementation than might look as follows:
public class WebApiTenantContext : ITenantContext
{
private readonly IHttpContextAccessor accessor;
private readonly IOptionsMonitor<TenantSettings> settings;
public WebApiTenantContext(
IHttpContextAccessor accessor,
IOptionsMonitor<TenantSettings> settings)
{
// Notice how the dependencies are not used in this ctor; this is a best
// practice. For more information about this, see Mark's blog:
// https://blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple/
this.accessor = accessor;
this.settings = settings;
}
// This property searches for the header each time its called. If needed,
// it can be optimized by using some caching, e.g. using Lazy<string>.
public string TenantId =>
this.accessor.HttpContext?.Request.Headers["tenant"].FirstOrDefault()
?? throw new Exception($"Request header tenant is missing");
}
Notice that this implementation might be a bit naive for your purposes, but hopefully you'll get the idea.
This class can be registered in the Composition Root of the Web API project as follows:
services.AddScoped<ITenantContext, WebApiTenantContext>();
Because the WebApiTenantContext has all its dependencies defined in the constructor, you can do a simple mapping between the ITenantContext abstraction and the WebApiTenantContext implementation.
For the Console application, however, you need a very different approach. The WebApiTenantContext, as shown above, is currently stateless. It is able to pull in the required data (i.e. TenantId) from its dependencies. This probably won't work for your Console application. In that case, you will likely need to manually wrap the execution of each message from the queue in a IServiceScope and initialize the ConsoleTenantContext at the beginning of that request. In that case, the ConsoleTenantContext would look merely as follows:
public class ConsoleTenantContext : ITentantContext
{
public string TenantId { get; set; }
}
Somewhere in the Console application's Composition Root, you will have to pull messages from the queue (logic that you likely already have), and that's the point where you do something as follows:
var envelope = PullInFromQueue();
using (var scope = this.serviceProvider.CreateScope())
{
// Initialize the tenant context
var context = scope.ServiceProvider.GetRequiredService<ConsoleTenantContext>();
content.TenantId = envelope.TenantId;
// Forward the call to the message handler
var handler = scope.ServiceProvider.GetRequiredService<IMessageHandler>();
handler.Handle(envelope.Message);
}
The Console application's Composition Root will how have the following registrations:
services.AddScoped<ConsoleTenantContext>();
services.AddScoped<ITenentContext>(
c => c.GetRequiredServices<ConsoleTenantContext>());
With the registrations above, you register the ConsoleTenantContext as scoped. This is needed, because the previous message infrastructure needs to pull in ConsoleTenantContext explicitly to configure it. But the rest of the application will depend instead on ITenantContext, which is why it needs to be registered as well. That registration just forwards itself to the registered ConsoleTenantContext to ensure that both registrations lead to the same instance within a single scope. This wouldn't work when there would be two instances.
Note that you could use the same approach for Web API as demonstrated here for the Console application, but in practice it's harder to intervene in the request lifecycle of Web API compared to doing that with your Console application, where you are in full control. That's why using an ITenantContext implementation that is itself responsible of retrieving the right values is in this case an easier solution for a Web API, compared to the ITenantContext that is initialized from the outside.
What you saw here was a demonstration of different composition models that you can use while configuring your application. I wrote extensively about this in my series on DI Composition Models on my blog.

Request Based Sticky session with Spring Cloud Loadbalancer not working as expected

I am using Spring reactor and WebClient for load balance purposes. I want to use the Request-based Sticky Session for LoadBalancer as follows:
Have service A that can invoke REST end points on service B or service C. Both B and C can have multiple instances of them running at a given time.
I want to use the standard round robin balancer for service B.
I want to use the request-based sticky session balancer for service C.
This is my code in the service A (that can invokes both service B and service C) through the WebClient. But it always ends up with round robin algorithm for service C. Can someone please
help me to figure out what I am doing wrong or missed to do? I referred to the below link
https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#request-based-sticky-session-for-loadbalancer
and also looked into the below SO
Request-based Sticky Session configuration with Spring Cloud LoadBalancer
#Configuration
public class WebClientConfiguration {
#Bean
#LoadBalanced
#Qualifier("loadBalancedWebClientBuilder")
WebClient.Builder builder() {
return WebClient.builder();
}
#Bean
#Qualifier("webClientBuilder")
WebClient webClient(WebClient.Builder builder) {
return builder.build();
}
}
application.yml (I am not very sure where the sticky-session definition should be added; i.e. service A or service C and hence repeated the below in both the services)
spring:
application:
name: service-A
cloud:
loadbalancer:
ribbon:
enabled: 'false'
sticky-session:
instanceIdCookieName: sc-lb-instance-id
addServiceInstanceCookie: true
Code in the service A to invoke service C with request based sticky session. I have a different file that does the same for service B (without the sticky session but uses the same WebClient Builder).
I know the service instance id that I want the WebClient to use to route the call.
#Component
public class ServiceCClient {
#Autowired
#Qualifier("loadBalancedWebClientBuilder")
private WebClient.Builder webClientBuilder;
public Mono<Void> triggerServiceCEndPoint(String paramA, String svcInstanceId) {
return webClientBuilder.baseUrl("http://service-C").build()
.post().uri(uriBuilder -> uriBuilder.path("/v1/example/end/point")
.queryParam("param-a",paramA).build())
.headers(httpHeaders -> {
httpHeaders.set("sc-lb-instance-id",svcInstanceId);
})
.cookie("sc-lb-instance-id", svcInstanceId)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Void.class);
}
}

Can I add a service info / health check endpoint to my Identity Server 3-based service?

I have a set of AspNet WebApi-based web services and an IdentityServer3-based authentication service. All of the web services support a simple service info endpoint that we use for monitoring and diagnosis. It reports the service version and the server name. The only service that currently does not support the service info endpoint is the IdentityServer3-based authentication service.
Is there a way to add a simple endpoint to an IdentityServer3-based service? In GitHub issue 812 Brock Allen says "We have a way to add custom controllers, but it's undocumented, current unsupported, and not really done." I'd rather not take that indocumented, unsupported route.
Is there a way to modify/extend the discovery endpoint to include additional information?
Here's how I ended up coding this up. At a high level, basically I added a Controllers folder, created a AuthenticationServiceInfoController class with a single GET action method and then registered that controller during Startup. As noted in comment above, my solution had some extra complexity because my AuthenticationServiceInfoController inherited from a base ServiceInfoController defined elsewhere, but I've tried to eliminate that from this sample. So, the controller code looks like this:
[RoutePrefix("api/v1/serviceinfo")]
public class AuthencticationServiceInfoController : IServiceInfoController
{
[Route("")]
[Route("~/api/serviceinfo")]
public IHttpActionResult Get()
{
try
{
ServiceInformation serviceInfo = new ServiceInformation();
serviceInfo.ServiceVersion = Global.serviceVersion;
return Ok(serviceInfo);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
}
It implements a simple interface:
public interface IServiceInfoController
{
IHttpActionResult Get();
}
And in my Startup.Configuration method where I configure Identity Server, I've got:
var idSrvFactory = new IdentityServerServiceFactory();
idSrvFactory.Register(new Registration<IServiceInfoController, Controllers.AuthencticationServiceInfoController>());
I think that's all that it took. It's in place and working in my Identity Server 3-based service.

Async REST Services using WCF WebApi

I want to know what is the opinion of you fellow Developers regarding WCF WebApi services.
In an N-tier application we can have multiple layers of services. We can have services consuming data from external services. In that scenario its worth to create Async Rest Services using WCF 4.0.
public interface IService
{
[OperationContractAttribute(AsyncPattern = true)]
IAsyncResult BeginGetStock(string code, AsyncCallback callback, object asyncState);
//Note: There is no OperationContractAttribute for the end method.
string EndGetStock(IAsyncResult result);
}
But with the release of WCF WebApi this approach is still required? to create async services?
How to host them in IIS/WAS/Self Hosting
looking forward for suggestion and comments.
Well What i feel,In order to create asynchronous operations in the latest WCF WebAPIs (preview 6) I can still use same pattern (Begin/End), but I can also use the Task programming model to create asynchronous operations, which is a lot simpler.
One example of an asynchronous operation written using the task model is shown below.
[WebGet]
public Task<Aggregate> Aggregation()
{
// Create an HttpClient (we could also reuse an existing one)
HttpClient client = new HttpClient();
// Submit GET requests for contacts and orders
Task<List<Contact>> contactsTask = client.GetAsync(backendAddress + "/contacts").ContinueWith<Task<List<Contact>>>((responseTask) =>
{
return responseTask.Result.Content.ReadAsAsync<List<Contact>>();
}).Unwrap();
Task<List<Order>> ordersTask = client.GetAsync(backendAddress + "/orders").ContinueWith<Task<List<Order>>>((responseTask) =>
{
return responseTask.Result.Content.ReadAsAsync<List<Order>>();
}).Unwrap();
// Wait for both requests to complete
return Task.Factory.ContinueWhenAll(new Task[] { contactsTask, ordersTask },
(completedTasks) =>
{
client.Dispose();
Aggregate aggregate = new Aggregate()
{
Contacts = contactsTask.Result,
Orders = ordersTask.Result
};
return aggregate;
});
}
[WebGet(UriTemplate = "contacts")]
public Task<HttpResponseMessage> Contacts()
{
// Create an HttpClient (we could also reuse an existing one)
HttpClient client = new HttpClient();
// Submit GET requests for contacts and return task directly
return client.GetAsync(backendAddress + "/contacts");
}
WCF Web API comes with an completely async HttpClient implementation and you can host in IIS and also completely sefhost.
For a async REST "service" scenario please read "Slow REST"

Custom WCF authentication with System.ServiceModel.ServiceAuthenticationManager?

I'm working on custom WCF authentication and authorization and found some articles about UserNamePasswordValidator and ServiceAuthorizationManager.
I also found clues about using a custom System.ServiceModel.ServiceAuthenticationManager (dead link ), but msdn does not tell a lot about it ( http://msdn.microsoft.com/en-us/library/system.servicemodel.serviceauthenticationmanager.aspx ).
So here I am: anyone knows more about ServiceAuthenticationManager ?
In general, how would you set up custom WCF authentication ?
You're right, the documentation on this is no help at all.
The way I have used this class is as follows. Override the Authenticate() method to:
Pull the authentication tokens (e.g. username/password) out of the incoming message
Authenticate the tokens and use them to create an IPrincipal object. This will be the principal that is used during the invocation of the service operation.
Add the IPrincipal object to the message.Properties collection so it can be used later in the WCF processing pipeline
You can't just set the thread principal at this point as it is changed later on by WCF.
The code in the ServiceAuthenticationManager.Authenticate() methods would look something like this:
public override ReadOnlyCollection<IAuthorizationPolicy> Authenticate(ReadOnlyCollection<IAuthorizationPolicy> authPolicy, Uri listenUri, ref Message message)
{
int tokenPosition = message.Headers.FindHeader("Token", "http://customnamespace.org");
string token = message.Headers.GetHeader<string>(tokenPosition);
IPrincipal user = new CustomPrincipal(token);
message.Properties["Principal"] = user;
return authPolicy;
}
Then you add a custom authorization policy that
Retrieves the IPrincipal from the message (using the System.ServiceModel.EvaluationContext.Current.IncomingMessageProperties collection).
Pushes the IPrincipal into the EvaluationContext.Properties collection
Makes claims based on the IPrincipal.IsInRole() method
The code in the IAuthorizationPolicy() method would look like
public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
IPrincipal user = OperationContext.Current.IncomingMessageProperties["Principal"] as IPrincipal;
evaluationContext.Properties["Principal"] = user;
evaluationContext.Properties["Identities"] = new List<IIdentity> { user.Identity };
IList<Claim> roleClaims = this.GetRoleClaims(user);
evaluationContext.AddClaimSet(this, new DefaultClaimSet(this.Issuer, roleClaims));
return true;
}
In the service behaviour configuration, you need to set principalPermissionMode="Custom" in order for WCF to set the IPrincipal as the principal on the executing thread for the actual service operation invocation.
<serviceAuthorization principalPermissionMode="Custom"...