In my project, we have a couple different back-end APIs/endpoints that are called by the same front-end page at different times. All of these endpoints are sort of related to the overall "theme" or "purpose" of the page, but they are called at different times, for different reasons, and return different data. The data returned by each endpoint varies a lot. Some fields require simple database queries, some require processing to generate, some require calls to external APIs, but they all serve this common "theme" of the page and are temporally coupled within their respective endpoints.
Here's the general architecture of ONE of these endpoints,
UpgradePreviewEndpoint. This one gets called when the user selects a target version that they wish to upgrade to:
The other endpoints follow the exact same architecture and share the same abstractions, so hopefully imagination will suffice. Each endpoint has a single
process method that takes a request object. The endpoint then asks an
IResponseAssembler to assemble a string response. The format of that response is irrelevant to the question, but currently it's JSON by default.
Underneath the response assembly component, I have a single gateway into all the business logic used by this page, regardless of which endpoint is accessing it. This is my facade, which is called
ServiceFacade in the diagram. Each concrete
IResponseAssembler implementation also has its own corresponding
I...Facade interface through which it interacts with the concrete
ServiceFacade implements all of these interfaces, one for each assembler.
Since there's only one concrete facade, and it's highly stable, I could have allowed all of the assemblers to depend on it directly. I could have also created a single interface for the facade and allowed all of the assemblers to depend on that one interface. However, since each assembler only needs to call a few methods on the facade, both of those approaches would violate the Interface Segregation Principle. What I've tried to do here is implement the ISP by using individual interfaces to carve out pieces of real estate inside the
ServiceFacade for each assembler to depend on. That way, none of them depend on anything they don't actually need.
However, I have seen many descriptions of the Facade Pattern state that it should provide a single interface into a complex subsystem. Segregating the interface seems to violate that axiom. On the contrary, splitting the concrete
ServiceFacade class into multiple entities seems like a really absurd idea in this case, since the only reason for having multiple endpoints is the temporal coupling of the GUI which is completely irrelevant at the service layer.
I would like the fine folks in this community to scrutinize this design and tell me if I am committing any egregious crimes, or if there is a better approach. This is not my first iteration with the design of these APIs, and I feel that I am on the right track now, but that feeling seems to get me into trouble often enough to warrant skepticism.