The before code shows tight coupling where services call each other directly. The choreography example uses an event bus where services subscribe and react to events independently, enabling loose coupling. The orchestration example uses a central orchestrator class that calls each service in order, providing explicit control over the process flow.
Before (No coordination):
class ServiceA:
def process(self):
# Directly calls ServiceB
ServiceB().process()
class ServiceB:
def process(self):
# Directly calls ServiceC
ServiceC().process()
class ServiceC:
def process(self):
print("Process complete")
After (Choreography with events):
class EventBus:
subscribers = {}
@classmethod
def subscribe(cls, event_type, handler):
cls.subscribers.setdefault(event_type, []).append(handler)
@classmethod
def publish(cls, event_type, data):
for handler in cls.subscribers.get(event_type, []):
handler(data)
class ServiceA:
def process(self):
print("ServiceA processed")
EventBus.publish('A_done', {})
class ServiceB:
def __init__(self):
EventBus.subscribe('A_done', self.process)
def process(self, data):
print("ServiceB processed")
EventBus.publish('B_done', {})
class ServiceC:
def __init__(self):
EventBus.subscribe('B_done', self.process)
def process(self, data):
print("ServiceC processed")
# Setup
service_b = ServiceB()
service_c = ServiceC()
# Start
ServiceA().process()
After (Orchestration):
class Orchestrator:
def __init__(self):
self.service_a = ServiceA()
self.service_b = ServiceB()
self.service_c = ServiceC()
def run(self):
self.service_a.process()
self.service_b.process()
self.service_c.process()
class ServiceA:
def process(self):
print("ServiceA processed")
class ServiceB:
def process(self):
print("ServiceB processed")
class ServiceC:
def process(self):
print("ServiceC processed")
# Start
orchestrator = Orchestrator()
orchestrator.run()