The cool thing (IMHO) is that this technique obsoletes ASP.NET MVC/WebApi and similar. That is, if you combine command/queries over ajax/websockets, you only need static HTML pages and a client side library like angular. To gain performance, you could even move all static HTML to a CDN.
Since this architecture doesn’t require a server side view engine, view model binding, controllers or whatever, the logic in the server is pretty slim which also allows more optimizations and performance gains. The HTTP server is functioning more like a service broker which redirects incoming messages to different parts of the system. The CQS Server could for instance route messages to different micro services as I’ve shown in a previous post.
Let’s go through all small pieces in my proof of concept. Do note that the library is still in an very early stage. The sample code can be watched in the github repos (link at the top of this article).
Authentication/Authorization
To authenticate, I’m just using BASIC authentication in this sample. It works great when you have SSL activated (otherwise it’s not secure at all). The user is not requested to authenticate unless requested by your code.
Authentication is divided into two parts. The first part is to do the actual authentication by using a IAuthenticator
, once completed a IPrincipalFactory
is used to assign a IPrincipal
to the current thread.
To activate authentication, you assign an authenticator to the listener:
var listener = new CqsHttpListener(builder.BuildMessageProcessor());
listener.Authenticator = CreateAuthenticator();
... which in this case is built like this:
private static IAuthenticator CreateAuthenticator()
{
var accountService = new SimpleAccountService();
accountService.Add("admin", "admin", new[] { "Admin", "User" });
accountService.Add("user", "user", new []{"User"});
var basicAuth = new BasicAuthentication(accountService, "localhost");
return basicAuth;
}
I just got some fake users. The IAccountService
interface (implemented by SimpleAccountService
) is tiny and easy to implement (using a DB, flat files or whatever).
Finally, you need to be able to authorize users.
Start by telling the library to use role based authorization:
GlobalConfiguration.AuthorizationFilter = new RoleAuthorizer();
Then you need to put the [Authorize]
attribute either on your CQS class, or on the handler class:
[Authorize("Admin")]
public class GetUsers : Query<GetUsersResult>
{
}
IoC Support
In this example, I’m using Autofac:
private static AutofacAdapter BuildContainer()
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterServices(Assembly.GetExecutingAssembly());
var container = containerBuilder.Build();
var adapter = new AutofacAdapter(container);
return adapter;
}
As you can see, I’m just using a single line to register all command/query handlers. It’s an extension method that is available in the Griffin.Framework.Autofac
nuget library. It registers all classes that are decorated with the [ContainerService]
attribute like this:
[ContainerService]
class GetUsersHandler : IQueryHandler<GetUsers, GetUsersResult>
{
public async Task<GetUsersResult> ExecuteAsync(GetUsers query)
{
return new GetUsersResult()
{
Items = new[]
{
new GetUsersResultItem
{
Id = 1,
UserName = "arne"
}
}
};
}
}
Finally, I assign the ioc container to the CqsHttpListener
.
var builder = new IocBusBuilder(adapter);
var listener = new CqsHttpListener(builder.BuildMessageProcessor());
Client Side
HTML page which is getting the query result:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Demo</title>
<script type="text/javascript"
src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
</head>
<body>
<script type="text/javascript">
$.ajax({
type: "POST",
url: 'http://localhost:8899/cqs',
data: { },
headers: {
'X-Cqs-Name': "GetUsers"
},
dataType: 'json'
}).done(function (data) {
console.log(data.Items);
});
</script>
</body>
</html>
Result without the [Authorize]
attribute:
... with the attribute...