If you imagine a monolith as a service that:
takes a request
-> deserializes it/unpacks it to a function call
-> sets up the context of the function call (is the user logged in etc)
-> calls the business logic function with the appropriate context and request parameters
-> eventually sends requests to downstream servers/data stores to manipulate state
-> handles errors/success
-> formats a response and returns it
The main problem I've seen in monoliths is that there is no separation/layering between unraveling a requests context, running the business logic, making the downstream requests, and generating a response.
Breaking things down into simplified conceptual components I think there is a: request, request_context, request_handler, business_logic, downstream_client, business_response, full_response
What is the correct behavior?
return request_handler(request):
request -> request_context;
business_response = business_logic(request_context, request):
downstream_client();
downstream_client();
business_response -> full_response;
return full_response;
business_response = request_handler(request_context, request):
return business_logic(request_context, request):
downstream_client();
downstream_client();
business_response -> full_response;
return full_response;
request -> request_context;
business_response = request_handler(request_context, request, downstream_client):
return business_logic(request_context, request, downstream_client):
downstream_client();
downstream_client();
business_response -> full_response;
return full_response;
something else?
In most monoliths you will see all forms of behavior and that is the primary problem with monoliths. Invoke any client anywhere. Determine the requests context anywhere. Put business logic anywhere. Format a response anywhere. Handle errors in 20 different ways in 20 different places. Determine the request is a 403 in business logic, rather than server logic? All of a sudden your business logic knows about your server implementation. Instantiate a client to talk to your database inside of your business logic? All of a sudden your business logic is probably manipulating server state (such as invoking threads, or invoking new metrics collection clients).
The point at which a particular request is handed off to the request specific business logic is the most important border in production.