-
Notifications
You must be signed in to change notification settings - Fork 17
Description
(Based on conversations with @pbontrager and @DNXie)
Currently, Forge users are exposed to 2 concepts: Services (spawns processes and replicas) and ForgeActor (class doing the "work"). Should we flatten this mental model so that users only think of everything as a Service instead?
From a user's perspective, Services are a wrapper around Actor instances, with the routing setup logic intentionally abstracted away from the user. The API for spawning a Service even literally takes the underlying Actor as an argument.
Option A: Just a rebrand
Service
=>ServiceRouter
ForgeActor
=>Service
- Nothing is functionally changed; just a rename
- @DNXie has started on something similar in [WIP] Renaming actor to service #124
async def spawn_service(
service_cfg: ServiceConfig, actor_def: Type[Service], **actor_kwargs
) -> ServiceInterface:
# Where ServiceInterface is mentally mapped as the functionality of both the Router and Service
Option B: Flatten the layers
Currently, the architecture is layered such that each class has a distinct responsibility:
ServiceInterface
wrapsService
and exposes select meta APIs from it's nested child classes (actor, replica, etc)Service
has aList[Replica]
and deals with routing and load balancing- Recall that the final Service implementation should be an Actor itself
Replica
hasForgeActor
and manages the request queueingForgeActor
has the actual endpoints that users write and adds LoggingActor
is your base Monarch Actor
This looks great from a "Single Responsibility Design" aspect, but should we flatten the overall architecture to reduce this depth? Effectively moving from "instantiate a new class and adding it as a class member" to "populating a field"
Specifically, combining the interfaces of Service
and ForgeAgent
, call it SuperService
for the sake of discussion
class SuperService(Actor):
# All the interfaces in ForgeActor
# These + @endpoints are the things only things users implement
setup()
launch()
shutdown()
# From Service
call()/call_all()
start()/stop_session()
replica: List[Replica] # This one is a little tricky since it wraps an actor
class MyService(SuperService):
@endpoint
async def foo(self):
print("a")
I haven't fully fleshed out the design/feasibility (@DNXie you should talk to @allenwang28 to flesh this out if we decide to do this)
Thoughts on the idea of making the idea of Service
the user mental model, regardless of approach?
Are the layers of Option B unavoidable?