0

I'm trying to design a new microservice architecture that will eventually replace our current two monolithic APIs (we develop a desktop payroll software that uses our API to send payroll documents to employees or have them sign contracts, and more features and front-ends to come).

There is a lot of complexity regarding users relationships and I'd like to future-proof my design as much as possible.

Please excuse me if I'm not clear enough, I'm not a native English speaker and there are a lot of fine details :)

TL;DR

I will give many details below in case you're interested, but here is a summary:

  • I have many types of actors who are tied with chained, business logic relationships (ex: Organization > Company > Employee).
  • Most of (maybe all in the future) these actors are candidates for being linked to a web user account. And a user account should be able to act like multiple actors (ex: an account X could act as a Company A, an Employee from Company A and an Employee from Company B).

And I hesitate between at least 2 solutions:

1) Creating a microservice per actor type

  • This could make sense because each actor have its own business logic (properties, actions).
  • But I'm afraid it will generate a lot of inter-services requests for basic actions (ex: DocumentService must check if the user account has rights to send Documents to an Employee, and this permission is defined as a Company business logic...).
  • Moreover, each microservice should be aware of others business logic, which greatly defeats the microservice philosophy in my opinion.

Option 1

2) Creating a generic UserManagement service

  • This could also make sense because I feel "actors and users relationships" could be seen as a business logic in itself, and all these actors are tightly coupled.
  • My main fear with this option is that this service could become bigger and bigger as we bring features to the web. But then again I could create new microservices for these features and keep using my UserManagementService as a sort of RBAC provider.

Option 2

What sounds more like a future-proof, best practice to you?

Do you have any advice or thoughts?

More details

Currently, these are the different types of actors in our system:

Types of actors

Please note these edge (but nonetheless common) cases:

  1. A Customer could be an accounting firm and manage payrolls for multiple Companies.
  2. A Customer could be managing its Company alone, so all these actors could be one single person. And the Customer could be one of their own Employees as well.
  3. Employees, being artists for the most part, often work for several Companies (which could be managed by different Organizations).

This is basically how our software works (locally, without APIs):

  1. Customer pays for their licence.
  2. Customer provides licence key to create an Organization (can create multiple ones).
  3. Customer creates SoftwareUsers inside this Organization, with different access levels (which determines which companies/employees one can read/write).
  4. SoftwareUsers can create Companies inside this Organization to manage their payroll.
  5. SoftwareUsers can create Employees attached to a single Company.
  6. SoftwareUsers can generate payslips for Employees.

Then, these are the web-based features that are implemented by our current APIs:

  1. SoftwareUser can invite an Employee to create their web account (on behalf of their attached Company).
  2. SoftwareUser can send PDF Documents to an Employee's account (on behalf of their attached Company).
  3. SoftwareUser can create a Campain to have an Employee sign a contract online (on behalf of their attached Company).

As for future features, there will be:

  1. Customer can pay for their licence online and view some data (remaining payroll volume, consumed phone support time, etc...).
  2. Companies (who are not using the software if the Customer is an accountant firm) can view Employees related to them, signed Campains and Documents generated on their behalf.
  3. SoftwareUsers who don't have access to the desktop software can enter data to populate payslips (this "feature" is expected to grow in the next years as we will progressively bring all the software's features to the web).

Finally, this system needs to be flexible, considering the following use cases:

  • Companies could be transferred to other Organizations (ex: a company could ask its accountant to provide its data if they wanted to manage their payroll itself).
  • The same principle applies to each actor type.
  • Account creation invitations (with Documents attached) could be sent to wrong email addresses, so we must be able to transfer an Employee to a different account.

These are the reason I was planning this for relationships and authentication:

  • For relationships, each actor will only know its parent ID and its linked Account(s) ID(s): this will enable to easily switch owners (a required feature).
  • For authentication, I'm planning to use JWTs with only the Account ID as payload.

UPDATE

I took your comments into consideration and talked with some teammates, here is a new option:

3) With a PermissionService

  • Accounts are not linked to "actors" anymore. Instead, they are linked to multiple Permissions that each specifies who has access to which resource, and possibly additional claims (ex: accessLevel for a Permission on an Organization).

  • This gives the possibility to provide either "account tokens" (with only the account ID), or "permission tokens" (with also the target ID and claims), depending on whether we enable SSO or need restricted service accounts.

  • As for the ActorService (not sure if the name fits well), its purpose is to hold the business logic regarding "actors relationships" as well as their properties (some of which were added on the diagram to illustrate their business value).

Option 3

I'm still not 100% sure about this design, so every comment is welcome.

2
  • Make no apologies about your written English, you're post is clear and understandable.
    – Ron Beyer
    May 7, 2020 at 14:27
  • I'm struggling a little to understand your proposal here. e.g. is CompanyService a microservice that is going to orchestrate calls to other services or is it simply evaluating authorizations? In the second proposal, is the UserManagement service again orchestrating calls?
    – JimmyJames
    May 7, 2020 at 20:14

3 Answers 3

1

Forget about the data. Systems need to be modeled according to behavior if you want any chance at something even remotely maintainable at scale.

Your diagrams only show the data your system is using, and it is confusing you because it is leading you to consider lines (arrows) of communication between entities that are actually all the same thing. Your data suggests that an Employee is a Customer (it carries a Customer._id either through a bunch of joins or explicitly depending on your database structure). Is that true? Don't answer that.

What I am getting at above is that you are mixing authorization into your schema. You have diagrammed five database tables that, together, hold only one single field of data that isn't a key ([accessLevel]). What if I proposed you replace all of your tables that act as a proxy for a human being with a single table: User [_id, accessLevel]?

Instead of worrying about how a Customer can interact with an Employee or whether a SoftwareUser is part of an Organization, just list out the use-cases for your application and implement them in a way that is completely agnostic as to "who" is invoking them.

Modeling the "Actors" within a domain, that is attempting to draw a line between the person that is operating the machine and each domain method, is going to lead to a world of pain and complexity. This is because there is always a person (or some proxy thereof) invoking each command, which means every single operation in your system is going to be complected with (dependent to) all of this information. It is no wonder that authorization is best left out of the domain.

To summarize, I would remove Customer and SoftwareUser entirely and replace them with a more traditional user/roles authorization schema (notably not part of your domain). The only value they add is as a proxy for "the person sitting at the computer" (look at your use-cases). This greatly simplifies your system.

As to how to organize the remaining Organization->Company->Employee hierarchy? These need to be on one box (unless to are okay with an eventually-consistent model).

2
  • Thank you for your comment, this is really interesting. I updated my post with a 3rd option using permissions, would you mind taking a look? I kept the Customer table because it is a business entity and not just a user.
    – Seeven
    May 12, 2020 at 9:15
  • @Seeven I don't know that I would separate the authorization into 3 distinct services (are you boundaries meant to delineate "microservices"?), but otherwise your updated design looks fine to me. May 12, 2020 at 18:34
0

I think you are making it unnecessary complex by linking Organisation and Company to login accounts. Both are legal entities that can not actually take an action themselves. They need a human to act on their behalf and you have such a human already identified in your system as SoftwareUser.

You can even take it a step further and conceptually separate the login account of an Employee from the Employee record that is owned by a Company. The only link that you actually need between the two is in the permissions (the "Login-Employee" is only allowed to access the "Company-Employee" documents) and maybe a back-link for sending notifications. That separation could even allow for the situation that a Company-Employee is declared legally incompetent and their guardian (also) has to be allowed access to the records.

2
  • Thank you for your comment, I get your point. I updated my post with a 3rd option using permissions and separating user accounts from the actors, would you mind taking a look?
    – Seeven
    May 12, 2020 at 9:21
  • @Seeven, your third option looks very reasonable. It also nicely separates the domain side (the things that the business talks about regardless of how it gets implemented) from the application side (the things that need to exist due to the choice for a computer-based implementation, like logins). May 12, 2020 at 12:14
0

I wouldn't do either of those things.

If you want to create a maintainable (i.e. future-proof) system you should not rely on details like data, and instead rely on abstract business concepts.

This is sometimes called vertical-, or feature-based splitting. This is the exact opposite of what you seem to be doing, which is basically having a microservice per data table.

Each microservice should implement specific business use-cases, and request-response calls between microservices should be minimized, ideally non-existent. If you use it in a web-context, integration can be easily done on the web-client (links, forms) instead of direct calls. Each microservice should own a complete process of some business-case.

This is the only way you can be sure that changes stay mostly localized and knowledge won't pile up in one or two "central" microservices.

To be clear, ideally microservices should contain/provide everything they need, the database, their UI, their authentication, etc. Of course you can be trade-offs here and there. But the more independent and self-contained you can get your microservices, the more easy it will be to maintain them later.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.