Three layers design
- Low level implementation Layer: using low level implementation to complete basic operation.
- For the network request, we can use the lib such as
axios
, which internally usingxhr
, or we can also usefetch
directly from node.js
- For the network request, we can use the lib such as
- request-core: Provide high-level network control features, including request serialization, request parallelism, and request deduplication.
- request-bus: Bind to the busniess logic
Current design is
- request-bus depends on request-core
- request-core depends on low level implementation layer
- low level implementation layer might use different built-in method or 3-rd party library
Impove the design
For low level implementation layer, as we can see it, the solution can be different
- xhr
- fetch
- axios
Which mean that this layer is not stable. Not stable means that once we might swap the current built-in methods or 3-rd party library for a better one in future. For example, if current you are using xhr
in the old project, you might want to upgrade it by using fetch
or axios
; or you found axios
is not good enough, you want to update to other libraray again in the further future again.
The problem is the low level implementation layer is a base layer. If this layer is not stable, any change happens in this layer might affect upper layer, such as request-core
layer. Which might further affect request-bus
layer.
Therefore, we need to find a solution can isolate this unstablness.
For that we can use a design pattern called DIP (Dpendence Inversion Principle), our aims is to decouple request-core
and low level implementation layer
.
Dependence Inversion Principle
- High level implementation should not rely on low level implementation details
- Both should rely on interface
Improved desgin
Now request-core
only provide interface, doesn't provide implementation.
// exmaple
export interface Requestor {
get(url: string, options: RequestOptions): Promise<Response>
//
}
let req: Requestor
export function inject(request: Requestor) {
req = requestor;
}
export function createParalleRequestor(maxCount = 4) {
const req = useRequestor()
// ...futher setting
return req
}
export function createSequenceRequestor() {
const req = useRequestor()
// ...futher setting
return req
}
request-axios-impl
example code
import { Requestor } from 'request-core'
import axios from 'axios'
const ins = axios.create();
export requestor: Requestor {
get(url, options) {
// use axios
},
// other methods
}
request-bus
example code
import {inject} from 'request-core'
import {requestor} from 'request-axios-impl'
inject(requestor)
In future, if there is any changes in low level implementation
layer, we don't need to change request-core
, we only need to add a new implementation, for example request-fetch-impl.ts
import {Requestor} form 'request-core'
export requestor: Requestor = {
get(url, options?) {
// using fetch
},
post(url, data, options?) {
},
// other methods
}
Only change in request-bus
layer
- import {requestor} from 'request-axios-impl';
+ import {requestor} from 'request-fetch-impl';
inject(requestor)