🕰️ Chapters
- Objectives & Project Familiarity
- What Are Fat Controllers?
- Fat Controllers & Services with Static Methods
- Completing Our Fat Controller Store Method
- Testing Our Store Method
- Fat Controller Pros/Cons
- Switching To Services
- Non-Static Service Methods
- Services & Dependency Injection (DI)
- The Service Approach
- Completing Our Service Store Method
- Splitting Out Sub-Tasks with Services
- Testing Our Service Store Method
- Service Approach Pros/Cons
- Services & Circular Dependencies
- Switching To Actions
- What Is An Action?
- The Action Approach
- Completing Our Action Store Class
- Splitting Out Sub-Tasks with Actions
- Testing Our Action Store Class
- Action Approach Pros/Cons
- Wrapping Up & Other Potential Options
Three Approaches for Organizing your AdonisJS Business Logic Operations
In this lesson, we'll dive deep into three different ways we can organize our code; fat controllers, services, and actions. We'll also discuss circular dependencies, static and non-static service methods, and dependency injection.
- Author
- Tom Gobich
- Published
- Jul 22, 24
- Duration
- 28m 8s

Developer & dog lover. I teach AdonisJS, a full-featured Node.js framework, at Adocasts where I publish weekly lessons. Professionally, I work with JavaScript, .NET, and SQL Server.
Adocasts
Burlington, KY
Get the Code
Download or explore the source code for this lesson on GitHub
-
1.0AdonisJS Authentication in 15 MinutesLesson 1.016m 7s
-
2.0How To Do Multi Model Authentication with AdonisJS and Lucid ORMLesson 2.018m 29s
-
3.0AdonisJS User Role Authentication in 15 MinutesLesson 3.015m 43s
-
4.0AdonisJS 5 API & Nuxt 3 SSR Authentication in 15 MinutesLesson 4.013m 58s
-
5.0AdonisJS 6 Session Authentication in 15 MinutesLesson 5.015m 32s
-
6.0AdonisJS 6 Access Token Authentication in 20 MinutesLesson 6.020m 29s
-
7.0Three Approaches for Organizing your AdonisJS Business Logic OperationsLesson 7.028m 8s
-
8.0How To Make A Simple AdonisJS 6 PackageLesson 8.025m 24s
-
How To Add Social Authentication with AdonisJS Ally & GoogleLesson 9.025m 30s
Join The Discussion! (2 Comments)
Please sign in or sign up for free to join in on the dicussion.
diego-sepulveda
Hey thanks for the tips. One question though, what would be your approach to avoid over-fetching data from DB, since the queries are hardcoded (inside services and actions). For example one view or endpoint could only require one field from DB, and another could require more. Would you accept params in the action to modify the query or would create more specific actions to adapt the query?
Please sign in or sign up for free to reply
tomgobich
Hi Diego! That's a great question! My approach would vary depending on just how segmented I would need to get at the data. Let's take a
Post
model as an example. A post might have certain types (blog, article, news, etc). If the type is the primary way I need to fetch the post, I might have agetPostsByTypeId(typeId: number)
service method or action.However, typically there are other checks needed for a post as well
Fetching only those that are published, not published, or all
Fetching posts tied to a specific taxonomy/topic
Ordering by publication date, title, or something else
Should we paginate the results or get only a specific number back
And, the list could go on from there. Our endpoints will vary from caring about none through all of these.
My approach to this is to stop and think about what most endpoints are going to need now or shortly in the future. I don't worry about issues we might have 3 or 5 years down the road, as those may or may not come to fruition and we can always refactor to appease them if they do.
I'd then create methods (service or action) to satisfy the requirements of most of the endpoints I'll need. This might result in:
getPosts(stateId: number, typeId: number, limit?: number = 10, orderBy: PostSorts = 'latest')
This would return the first
limit
number of published posts for the provided type and state ordered by sorts we have defined for our posts (like latest, alphabetical, popular, etc). Type would be whether it's a blog, article, etc and state would determine if it's published or not.getPostsPaginated(typeId: number, pagination: PaginatorQuery, orderBy: PostSorts = 'latest')
This would be similar to
getPosts
, but would return a paginator instead of the firstlimit
.PaginatorQuery
here would contain thepage
,perPage
, andbaseUrl
That alone would satisfy most of the way we would need to get at our posts, however, a really nice part about Lucid's query builder is that the query can be built on top of further until it is either awaited, executed, or we call a method that does not return back the builder, like
paginate
.Meaning, we could return the Post Query Builder from these methods giving us the ability to expand off of them as needed, which is really convenient for those outliers. We might also choose to call these
queryPosts
instead ofget
to note that they're expandable. For example:Now, with this in mind and also that
paginate
won't allow this, we could remove thelimit
parameter from this method, get rid of ourgetPostsPaginated
method and instead perform those outside of these methods for additional expansiveness.If desired, we can create additional methods that call and build off our
getPosts
as well. Like, the above blog query could be wrapped in agetPostsByTopic(slug: string)
method. This comes in handy if this is a frequent way we're going to get at our posts. If we're only ever going to need this for a single page, we might chose to leave the query as-is from the above previous example.I also really like giving the more complex statements in my queries names. This can be done via query scopes or you can go a step further and create a builder. A builder is essentially a query builder wrapping the Model Query Builder itself with chainable methods specific to your application. That might look like the below.
What I like about this is that it keeps our queries very easy to build and understand. It also keeps any methods we may choose to wrap this with small and easily reusable as well. Which, in turn, makes it less of a chore to have multiple different methods for getting at our posts. This is actually the approach I've used for the Adocasts site, if you'd like to see a full example.
Sorry this was a long answer, TLDR: the best approach will vary depending on how many varying ways you need to query your data. If it's only a few ways, specific methods will do just fine. If you need it a myriad of different ways, more complex or even a combination of solutions may prove fruitful.
Please sign in or sign up for free to reply