Autonomy is important but I suspect that you are primarily talking about runtime autonomy - design/maintenance time autonomy can be even more important. I suspect that your view on dependencies needs a slightly more measured approach.
Ticket Customer should absolutely minimize any dependencies on the Ticket Service, i.e. it should be loosely coupled. Meanwhile Ticket Agent, Agent Supervisor, and Ticket Vault are working together towards implementing the responsibilities of the Ticket Service - that is their job, so they need to be interdependent to work toward their common goal - "dispensing tickets in accordance with the rules of the service". So within the boundary of the Ticket Service those three are subject to high cohesion and high coupling because they need to share certain details about the "dispensing business" that are "nobody's business" outside of that boundary. As long as the Ticket Customer remains oblivious to these "business details", Ticket Service can change the "internal business practices" with impunity - e.g. switch implementations from Scenario B to Scenario A or vice versa.
Also I'm not arguing against autonomy over state, as I said before, state is unavoidable but when it appears it is worth scrutinizing whether it is necessary and whether it appeared in the right place.
Ultimately I was responding to this:
one process per user to hold state
You seem to be more concerned about where "state" goes rather than "what you are trying to accomplish".
State shouldn't be the primary design concern - are you "getting done", what needs to be done? - that usually is accomplished by dividing up the responsibilities (not state). A message-based system works by moving data (events) from process to process - that message data and its movement is what is important.
Some processes will have state as a result of their responsibility and on the most general level a process is a message processor first and a state container second (and only if absolutely necessary). A "user" is a concept that may entail many responsibilities - so those responsibilities could well be spread across multiple processes - some of them possibly handling multiple or even all users if that is what is necessary to fulfill that particular responsibility.
It would be a mistake to select a single process as a locus of state and then aggregate all the responsibilities that need access to that state into that process like this:
- one process per user to hold state
- that process will periodically/asynchronously persist the state to the database just in the case
I see that and I see a process version of Active Record. Mixing responsibilities was a bad idea with objects and still is a bad idea with lightweight processes.
if the bot doesn't remember context, the user will have to pass the entire conversation history every time, that doesn't seem right. If the chat bot keeps a log of their chat history and reads it very quickly every time to build up context, should we provide an external storage for the chat bot to store that log just for the sake of keeping the bot stateless?
Keeping the bot "stateless" has advantages and disadvantages. First of all there is no need to pass the "entire conversation" for the purpose of following a chat. A client should be perfectly capable of ordering a list of sequenced chat items as they are broadcast and dogmatic statelessness would make it impossible to join a chat.
So at the very least there must be a serverside concept of a "conversation" that clients can join and receive broadcasts from. Now all the chat items could become part of that "conversation state" but that wouldn't be broadcast with every new chat item though it may be sent to newcomers as they join a chat late.
But the "conversation" is a separate state from the client states even though the "conversation" relates to the clients it broadcasts to and the clients relate to the "conversations" they are participating in (and the "full" client state may not even exist in the "Elixir space").
For me this topic suggested a Carte Blanche "all server side state is OK" free-for-all that made no attempt to justify why any type of state needed to exist in the first place.
What you describe is a process with a clearly defined (narrow) responsibility where its state is (private and) essential to the fulfillment of it's objective. I would also expect that the process "outsource" any "real work that could fail" in order to protect integrity of that state i.e. launch a separate process with just enough information to perform the download.‡
There is nothing wrong with that kind of state. What I'm cautioning against is state-oriented design which borders on "object-thinking".
(‡ As a design guideline I favour short-lived processes simply to minimize the possibility of corruption of their state. However there will always be long-lived processes with state. Again to minimize corruption of state these processes should do as little as possible. However they shouldn't simply be containers of state. They should be smart enough to take a request, augment it's data with information from the process state and forward the actual work to "somewhere safe".)