While reading Apollo's doc for Relay Connection pagination I encountered a new terminology, or let's call it as Wikipedia does, a new rule of thumb. But here I prefer to consider it a principle. So from now on I'll call it principle.
ZOI -- Zero, One, Infinity Principle
I continued my quest on uncovering this new term and read some interesting posts on Medium, such as "The “Zero, One, Infinity” rule" written by Tal Joffe. But frankly speaking I still felt it is better to share my understanding here and see what you think about is topic.
So in software engineering this principle acts as some sort of compass. IMO it help us to decide when we need to add a new layer of indirection, or just go with the bare minimum.
In essence it says we should not restrict how many of a thing (method, instance, etc) can exist in our system. We either have:
- Zero: You prohibit that thing from existing entirely. Like when you say no freaking way that the root directory in Linux to be placed in another directory (or at least that's how I feel about Linux's root directory).
- One: Only one instance of something can exists: like root user, we only have one.
- Infinity: Create as many as you desire. It's all good. Create as many1 files as you wanted in a directory.
Let's talk more about its real life implications, rather than theory.
Repository Pattern and ZOI ;)
I know you're gonna love this, in fact I am just sharing my take on what Tal Joffe said in his post on Medium.
-
He starts by saying that assume we have
OrderRepository
class:
class OrdersRepository{ getOrders(ids: number[]): Order[]{ //... } }
-
But over time we most likely have something like this:
class OrdersRepository{ getOrders(ids: number[]): Order[]{ //... } searchOrders(searchTerm: string): Order[]{ //... } getOrdersNames(ids: number[]): string[]{ //... } // for the full example read his post. Linked at the beginning of this post. }
So now imagine you wanna add a new feature and need to have a new query which returns a list of orders. Now first we might look up in our current implementation to see if we have one that matches our requirements. If not we will add yet another one!
Solution to Our Repository Pattern Dilemma
Recall, in ZOI we have 3 state, zero, one, or infinity. Here we have passed the zero point and if we wanna maintain only one getOrders
we need to somehow extract the responsibility of building the query away from this method.
In other words it should be only responsible for fetching data from the underlying database (SRP -- Single Responsibility Principle from SOLID). To do this we need to add a new layer on indirection. We need to do this since we need to follow Open/Close Principle too. We will create a new query builder using the builder pattern2
Something like this (TBH I have not find the time to work with this yet, so I love to read your comments how you have done this in your project):
interface QueryBuilder{
build(): any
}
interface Repository<T>{
getItems(queryBuilder: QueryBuilder): T[]
}
Relay Connection Pagination
Whenever we have a list in our API we most likely sooner or later run into the problem of needing pagination. So with that in mind the next question will become how it relates to ZOI.
ZOI says if you do not wanna allow it do not allow it and that's fine (Zero scenario). Or maybe you just wanna have one getOrders
and that's fine too (One scenario). Or like here we'll have an infinite number of records, so what ZOI suggest is to handle this scenario gracefully.
Meaning if you do not follow ZOI's suggestion the likelihood of building our GraphQL schema like this is high:
type Post {
id: ID!
comments: [Comment!]!
}
Whereas we might have developed it like the following when we have the ZOI principle in mind:
interface Node {
id: ID!
}
type Post {
id: ID!
commentsConnection(
first: Int
last: Int
after: ID
before: ID
): CommentConnection!
}
type CommentConnection {
edges: [CommentEdge!]!
pageInfo: PageInfo!
}
type Comment implements Node {
id: ID!
content: String!
}
type CommentEdge {
cursor: ID!
node: Comment!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: ID
endCursor: ID
}
Learn more about pagination here.
If this was useful to you consider giving me a star on GH on one of my repos that you've found it helpful.
BTW Follow me:
Instagram: https://www.instagram.com/node.js.developers.kh/
Facebook: https://www.facebook.com/kasirbarati
X: https://x.com/kasir_barati
YouTube: https://www.youtube.com/@kasir-barati
GitHub: https://github.com/kasir-barati/
Dev.to: https://dev.to/kasir-barati
LinkedIn: https://linkedin.com/in/kasir-barati
-
I know practically we cannot create "as many" as we wanted files in a directory since there are some other factors that restrict us from doing that (learn more here. BUT the point that ZOI is trying to make it clear is, that we ain't gonna restrict users by some arbitrary numbers. ↩
-
You can learn more about Builder Pattern here and see how I use it in practice here. ↩
Top comments (0)