By "security" I mean data access rights, for example:
Andrew only has read-only access to clients in France
Brian can update clients in France and Germany
Charles is an administrator, he has read and update rights for everything
I can see potential arguments for each layer.
Data Access Layer
The DAL only exposes clients to which the user has access, and passes an appropriate error up to the business layer when the user tries to do something unauthorised.
This simplifies the upper layers, and can reduce the data traffic for users who only have access to a small fraction of the data.
Business Layer
Because this is where the business logic resides and only the business layer has the complete knowledge of how the security should be implemented.
UI Layer
A tangent argument is because the UI layer is the one that deals with authentication.
A stronger argument is when the application has non-UI functions: calculating the daily P&L, archiving, etc. These programs don't have a security context and creating a fictitious 'system' user is a maintenance nightmare.
A separate layer?
Slotted somewhere inside the 3?
I'm looking for a cogent argument which will convince me that layer X is the best for large-scale 3-Tier applications. Please refrain from 'it depends' answers ;-).
Thanks.
I guess this may be a subjective topic. Nevertheless, we follow the principle to never trust any external source (e.g. data crossing a service boundary). Typically, modern applications are a bit different from the old client-server three-tier model, since they are usually service-oriented (I see a web server is also as a service).
This rules out the delegation of access checks to the client - the client may know about the allowed access and use this information to behave differently (e.g. not offer some functionality or so), but in the end only what the service (server) decides to allow counts.
On the other hand, the database or DAL is too low, since most checks also depend on some business logic or on external information (such as user roles). So this rules out the data layer; in our environments the data access is a trusted space that does not do any checks. In the end, the DB layer and the application server form a logical unit (one could call it a fortress - as per Roger Sessions Software Fortresses book), where no service boundary exists. If the app layer accesses another service however it has to perfom checks on the received data.
In summary, you might want to get a copy of Roger Sessions book because it does give some valuable input and food for thought on large-scale applications and how to deal with security and other issues.
Related
I am new to DDD and this is the first project for me to apply DDD and clean architecture,
I have implemented some code in the domain layer and in the application layer but currently, I have a confusion
now I have an API which is placeOrder
and to place the order I need to grab data from other microservices, so I need to grab production details from product service and address details from user profile microservice
my question is, should these data are pulled into the domain layer or in the application layer?
is it possible to implement a domain service that has some interfaces that represent the product service API and use that interface in the domain layer and it will be injected later using dependency injection or should I implement the calling of this remote API in the application layer?
please note that, calling product service and address service are prior steps to create the order
Design is what we do when we want to get more of what we want than we would get by just doing it. -- Ruth Malan
should these data are pulled into the domain layer or in the application layer?
Eric Evans, introducing chapter four of the original DDD book, wrote
We need to decouple the domain objects from other functions of the system, so we can avoid confusing the domain concepts with other concepts related only to software technology or losing sight of the domain altogether in the mass of the system.
The domain model doesn't particularly care whether the information it is processing comes from a remote microservice or a cache. It certainly isn't interested in things like network failures and re-try strategies.
Thus, in an idealized system, the domain model would only be concerned with the manipulation of business information, and all of the "plumbing" would be somewhere else.
Sometimes, we run into business processes where (a) some information is relatively "expensive" to acquire and (b) you don't know whether or not you need that information until you start processing the work.
In that situation, you need to start making some choices: does the domain code return a signal to the application code to announce that it needs more information, or do we instead pass to the domain code the capability to get the information for itself?
Offering the retrieve capability to the domain code, behind the facade of a "domain service" is a common implementation of the latter approach. It works fine when your failure modes are trivial (queries never fail; or abort-on-failure is an acceptable policy).
You are certainly going to be passing an implementation of that domain service to the domain model; whether you pass it as a method argument or as a constructor argument really depends on the overall design. I wouldn't normally expect to see a domain entity with a domain service property, but a "use case" that manipulates entities might have one.
That all said, do keep in mind that nobody is handing out prizes for separating domain and application code in your designs. "Doing it according to the book" is only a win if that helps us to be more cost effective in delivering solutions with business value.
The problem I'm facing is the design of an integration platform.
The company has different tools used for selling online financial services and wants to unify the selling process by creating a common integration platform.
Existing tools range from simply designing a tailor-made offer, to managing all the phases of listing to selling and supporting. The integration platform should orchestrate all the tools.
So I do approach this problem from a DDD point of view?
Domain: selling online services
subdomains: service catalog, request offers, sending offers, buying service, support customer.
bounded context? maybe integration with other company systems like identities and invoices?
My trouble with this is that some existing applications encompass several subdomains, others don't. Also, some applications working in the same subdomain have completely different languages, for example, service vs product, vs project...
How does an integration platform fit in this picture and how would you approach it from a DDD point of view? (or maybe it's a completely wrong approach and should I leave DDD inside each tool and treat them as bounded context?)
I recommend extracting the common bits of meaning (ignoring their names) from the various applications into common domains/bounded contexts. Each bounded context has anti-corruption layers that essentially adapt the language used in one or more existing applications to the one used in the common domain (and vice versa). Then you can cut over the existing applications piece-by-piece to use the respective ACLs to take advantage of the common domain implementation.
Eventually, you might even be able to dispense with the ACLs, as the language becomes more ubiquitous, but it's also perfectly okay to keep them around forever: the ACLs introduce some indirection (and possibly complexity, e.g. if they're deployed as their own microservices) but that's the price you pay for limiting coupling to the ACL.
(It's not clear from the question how experienced you are with DDD).
I am working on implementing a microservices-based application using node.js. While searching for examples on how to implement the api gateway, I came across the following article that seems to provide an example on implementing the api gateway: https://memz.co/api-gateway-microservices-docker-node-js/. Though, finding example for implementing the api gateway pattern in node.js seems to be a little hard to come by so far, this article seemed to be a really good example.
There are a few items that are still unclear and I am still have issues finding doc. on.
1) Security is a major item for the app. I am developing, I am having trouble seeing where the authentication should take place (i.e. using passport, should I add the authentication items in the api gateway and pass the jwt token along with the request to the corresponding microservice as the user's logged in information is needed for certain activities? The only issue here seems to be that all of the microservices would need passport in order to decrypt the jwt token to get the user's profile information. Would the microservice be technically, inaccessible to the outside world except through the api gateway as this seems to be the aim?
2) How does this scenario change if I need to scale to multiple servers with docker images on each one? How would this affect load balancing, as it seems like something would have to sit at a higher level to deal with load balancing?
I can tell that much depends on your application requirements. Really.
I'm now past the 5 years of experience in production microservices using several languages going from medium to very large scale system.
None of them shared the same requirements, and without having a deep understanding of what you need and what are your business (product) requirements it would be hard to know what's the right answer, by the way I'll try to share some experience to help you get it right.
Ideally you want the security to be encapsulated in an external service, so that you can update and apply new policies faster. Also you'll be able to deprecate all existing tokens should you find a breach in your system or if someone in your team inadvertedly pushes some secret key (or cert) to an external service.
You could handle authentication on each single service or using an edge newtwork tool (such as the API Gateway). Becareful choosing how to handle it because each one has it's own privileges:
Choosing the API Gateway your services will remain lighter and do not need to know anything about the authentication steps, but surely at some point you'll need to know who the authenticated user is and you need some plain reference to it (a JSON record, a link or ID to a "user profile" service). How you do it it's up to your requirements and we can even go deeper talking about different pros and cons about each possible choice applicable for your case.
Choosing to handle it at the service level requires you (and your teams) to understand better about the security process taking place (you can hide it with a good library) and you'll need to give them support from your security team (it's may also be yourself btw you know the more service implementing security, the more things you'll have to think about to avoid adding unnecessary features). The big problem here is that you'll often end up stopping your tasks to think about what would help you out on this particular service and you'll be tempted to extend your authentication service (and God, unless you really know what you're doing, don't add a single call not needed for authentication purposes).
One thing is easy to be determined: you surely need to think about tokens (jwt, jwe or, again, whatever your requirements impose).
JWT has good benefits, but data is exposed to spoofing, so never put in there sensitive data or things you wouldn't publicly share about your user (e.g. an ID is probably fine, while security questions or resolution to 2FA would not). JWE is an encrypted form of the spec. A common token (with no meaning) would require a backend to get the data, but it works much like cookie-sessions and data is not leaving your servers.
You need to define yourself the boundaries of your services and do yourself a favor: make each service boundaries clean, defined and standard.
Try to define common policies and standardize interactions, I know it may be easier to add a queue here, a REST endpoint there, a RPC there, but you'll soon end up with a bunch of IPC you will not be able to handle anymore and it will soon catch your attention.
Also if your business solution is pretty heavy to do I don't think it's a good idea to do yourself the API Gateway, Security and so on. I'd go with open source, community supported (or even company-backed if you have some budget) and production-tested solutions.
By definition microservice architectures are very dynamic, you'll fight to keep it immutable between each deployment version, but unless you're a big firm you cannot effort keeping live thousands of servers. This means you'll discover bugs that only presents under certain circumstances you cannot spot in other environments (it happens often to not be able to reproduce them).
By choosing to develop the whole stack yourself you agree with having to deal with maintenance and bug-discovery in your whole stack. So when you try to load a page that has 25 services interacting you know it may be failing because of a bug in: your API Gateway, your Security implementation, your token parser, your user account service, your business service A to N, your database service (if any), your database load balance (if any), your database instance.
I know it's tempting to do everything, but try to keep it flat and do what you need to do. By following this path you'll think about your product, which I think is what's the most important think to do now.
To complete my answer, about the scaling issues:
it doesn't matter. Whatever choice you pick it will scale seamlessly:
API Gateway should be able to work on a pool of backends (so from that server you should be able to redirect to N backend machines you can put live when you need to, you can even have some API to support automatic registration of new instances, or even simples put the IP of an Elastic Load Balancer or HAproxy or equivalents, and as you add backends to them it will just work -you have moved the multiple IPs issue from the API Gateway to one layer down).
If you handle authentication at services level (and you have an API Gateway) see #1
If you handle authentication at services level (without an API Gateway) then you need to look at some other level in your stack: load balancing (layer 3 or layer 7), or the DNS level, you can use several features of DNS to put different IPs to answer from, using even advanced features like Anycast if you need latency distribution.
I know this answer introduced a lot of other questions, but I really tried to answer your question. The fact is that you need to understand and evaluate a lot of things when planning a microservice architecture and I'd not write a SLOC without a very-written-plan printed on every wall of my office.
You'll often need to go mental focus and exit from a single service to review the global vision and check everything is going fine.
I don't want to scare you, I'm rather trying to make you think to succeed.
I just want you to make sure you correctly evaluated all of the possibilities before to decide to do everything from scratch.
P.S. Should you choose to act using an API gateway be sure to limit services to only accept requests through it. On the same machine just start listening on localhost, on multiple machines you'll need some advanced networking rule depending on your operating system.
Good Luck!
Since the most similar questions are related to ASP MVC I want to know some common right choice strategies.
Lets try to decide, will it go into the business layer or sit on the service layer.
Considering service layer to have a classical remote facade interface it seems to be essential just to land permission checks here as the user object instance is always here (the service session is bound to the user) and ready for .hasPermission(...) calls. But that looks like a business logic leak.
In the different approach with an implementation of security checks in the business layer we pollute domain object interfaces with 'security token' arguments and similar things.
Any suggestions how to overcome this tradeoff or maybe you know the only true solution?
I think the answer to this question is complex and worth a bit of thought early on. Here are some guidelines.
The service layer is a good place for:
Is a page public or only open to registered users?
Does this page require a user of a specific role?
Authentication process including converting tokens to an internal representation of users.
Network checks such as IP and spam filters.
The business layer is a good place for:
Does this particular user have access to the requested record? For example, a user should have access to their profile but not someone else's profile.
Auditing of requests. The business layer is in the best situation to describe the specifics about requests because protocol and other details have been filtered out by this point. You can audit in terms of the business entities that you are setting policy on.
You can play around a bit separating the access decision from the enforcement point. For example, your business logic can have code to determine if a user can access a specific role and present that as a callback to the service layer. Sometimes this will make sense.
Some thoughts to keep in mind:
The more you can push security into a framework, the better. You are asking for a bug (and maybe a vulnerability) if you have dozens of service calls where each one needs to perform security checks in the beginning of the code. If you have a framework, use it.
Some security is best nearest the network. For example, if you wish to ban IP addresses that are spamming you, that definitely shouldn't be in the business layer. The nearer to the network connection you can get the better.
Duplicating security checks is not a problem (unless it's a performance problem). It is often the case that the earlier in the workflow that you can detect a security problem, the better the user experience. That said, you want to protect business operations as close to the implementation as possible to avoid back doors that bypass earlier security checks. This frequently leads to having early checks for the sake of UI but the definitive checks happening late in the business process.
Hope this helps.
This is a fundamental design question about the service layer in my application, which forms the core application functionality. Pretty much every remote call reaches a service sooner or later.
Now I am wondering if
every service method should have a User argument, for which the operation should be performed
or if the service should always query the security implementation, which User is currently logged in, and operate on that user
This is basically a flexibility vs security decision, I guess.. What would you do?
There is also a DoS aspect to consider.
One approach is to offer (depending on your context) a publicly available instance / entry point to the services, on a well throttled set-up; and a less restricted instance to an internal trusted environment.
In a similar vein, if you identify where traffic originates you can (or should) be able to provide better QoS to trusted parties.
So, I would possibly keep the core system (the services you write) fairly open / flexible, and handle some of the security related stuff elsewhere (probably in the underlying platform).
Just because you write one set of services doesn't mean you can only expose those in one place and all at the same time (to the same clients).
I think you should decide which methods will need a user argument and which will need a logged in user. You'll get the following method types as a result for this:
1.) Type1: Method is best to have a User argument.
2.) Type2: Method is best to not have a User argument.
3.) Type3: A combination of 1.) and 2.)
The solution of 1.) and 2.) is simple, because they are trivial cases.
The solution of 3.) is to overload the method to have a version of 1.) type and another version of 2.) type.
I try to look at security as an aspect. User argument is required for things other than authentication as well. But, I think control should reach the service layer's more important methods only if the user has been authenticated by some other filter. You can't have every method in the service layer querying the security module before proceeding.