Should an artist paint based on commissions, or should they paint what they want and then sell it?
Should a contractor build prefab houses, or should they build custom blueprints?
Should a doctor be visited by their patients, or should they visit their patients?
All of these questions are the same. There is no specific answer to them. It depends on what your particular goal is. Maybe you have a clear opinion on all of these. Maybe you don't.
For example, one of the clients I'm working for has had bad experiences with workloads involving customization for different clients (and the conflicts arising from it), and has now adopted a very stark "we build it the way we do, the consumer just has to accept that" attitude. Their codebase never considers a request from a specific client, at best they address issues as being for "clients", i.e. everyone at the same time. E.g. "clients are asking for the ability to do X" as opposed to "Client A is asking for the ability to do X". Either everyone gets it or no one does. It keeps things really simply but it does turn some customers off.
On the other hand, a previous client I worked at specifically prided themselves on their ability to adapt to a customer's specific needs and would cater to their every whim, at the cost of the development workload this entails.
Neither of them are wrong, they're just in a different business. This is why we often speak about "products" versus "solutions" in terms of development strategy.
Products are built based on their own needs, and are finished without any particular customer in mind. Solutions are tailor-made to a specific customer, and that includes cases where the groundwork is a reusable product that is then tweaked to the customer's specifications.
So, are you building a product or a solution?
As a baseline, your software has two endpoints:
- Fetching tasks
- Editing a task
The two are unrelated to each other. It would be perfectly viable (from your end) to let the customer figure out which task they can edit and which they can't.
That is of course not a great user experience. But it does achieve the first (absolutely barebones) milestone: you have a working backend.
Next, you get to decide how much time and effort you spend on making your backend nicer to work with, and in which way. This can come in many different shapes and forms:
- Returning the status when fetching the tasks
- Allowing pre-emptive filtering of tasks based on status
- Returning a
isEditable flag in your returned task data
There is no universal solution here. It all depends on what you need.
- For example, if no one would ever use the filtering except to figure out if something can be edited, that's a really inefficient way of going about it, e.g. when showing a list of all tasks, some of which can be edited.
- Then again, if users spend a lot of time fetching the tasks, a filtered endpoint is going to add much usage value to your API.
- Then again, if we're never talking about more than a handful of tasks (10 or less) being returned, then the list is too short to really benefit from the complex filtering and you're better off just returning extra data in the task objects.
As you can see, there are plenty of situations in which one approach can be better than another, but there is no universally superior solution.
There will likely come the requirement to develop different clients that e.g. require work-task regardless of status
We cycle back to the original question. Is your backend a product or a solution?
If you intend to have different frontends (one per client) which all use the same backend, then either:
- Client-specific rules would need to live on the frontend (ease of development, but not secure
- OR Your backend needs to expand to cater to every single client at the same time (secure, but takes more effort)
If you want a high degree of customization in your backend as well, then it may be better to simply split off the backends (one per client) so that you don't have to deal with conflicts between different clients' rules. But it takes more deployment overhead to do so.
Or, you can instead merge it all into the same codebase to save on deployment overhead cost, at the cost of development complexity.
Most things in software development are a trade-off. Depending on what your company is willing to invest in or what they already have, certain decisions may be more desirable than others, but that doesn't always translate to other companies/people.
- Design an abstract API and allow the client to filter for status='active' in the query. The goal is to make the API easily reusable. That includes that the client developer needs to know part of the use case (e.g. the kind of status he needs to filter). It also means that if the business logic ever changes (e.g. another editable status is being introduced, say, partly-active), the client would need to be changed (to include that value into his query filter).
Your main issue here can be avoided by not using an "active" filter to solve your problem, but rather to rely on the concept of a task being "editable" (a calculated property on the task, based on your validation rule). The backend clearly defines the editability of an object, so it can rely on its own knowledge to decide whether a task is editable or not.
This can take the shape of having an "editable" filter on the request, where you only return the editable tasks (and without the filter, you return all tasks).
Or, alternatively, you can add an
isEditable field to the tasks you return, so that it is immediately known if a specific task is editable or not.
Personally, I'd implement both. It gives you the best of both worlds. The filter means you transport less data when the consumer doesn't care about the ineditable tasks, and the
isEditable flag means that regardless of the filter being activated or not, the frontend is always acutely aware whether a task can be edited or not.
That way, the frontend just asks the backend for the editable objects, and the backend gets to decide what the criteria for being editable are. If you change your criteria for editability tomorrow, you only need to redeploy the backend, and the frontend doesn't need to be changed.
a) Include a technical field into the resource representation that specifies the client, say forClient. The client would then filter that property for e.g. forClient='App1'.
b) Include a 'technical association', between a parent resource and the actual resource which would lead to the query look something like this: work-package/forApp1/work-tasks. Work-package would be an object that has a 1:n relationship to work-task.
Be very careful about these kinds of fields (whether in the URL or the request body is irrelevant for my point).
This allows any caller to arbitrarily change the client they're representing. While it might take some guesswork as to which clients exist, if this is the summation of your security, it is easily circumventable and end users will find ways around their own business rules by pretending to be another client.
It would be much more secure to define the client based on the user account. This way, you know that your business rules (and the choice of client thereof) is at least as secure as your authorization platform itself is.
c) Develop a new completely new API per client.
That's definitely an option, but we get back into the "how much effort are you willing to spend, and what's the gain" line of reasoning.