DEV Community

Cover image for Domain, Service and IoC
Marcos Filho
Marcos Filho

Posted on

Domain, Service and IoC

All the code's that you will see in this post you can check here

To keep building our project, in this post we will build our service layer, domain model and our dependency injection container.

The structure from this project in this post will looks like this

📦hero-api-golang
 ┣ 📂cmd
 ┃ ┗ 📂http
 ┃ ┃ ┣ 📂handler
 ┃ ┃ ┃ ┣ 📜book.go
 ┃ ┃ ┃ ┗ 📜router.go
 ┃ ┃ ┗ 📜main.go
 ┣ 📂internal
 ┃ ┣ 📂container
 ┃ ┃ ┗ 📜container.go
 ┃ ┣ 📂domain
 ┃ ┃ ┗ 📂book
 ┃ ┃ ┃ ┗ 📜model.go
 ┃ ┗ 📂service
 ┃ ┃ ┗ 📜book.go
 ┣ 📜.gitignore
 ┣ 📜go.mod
 ┣ 📜go.sum
 ┗ 📜README.md
Enter fullscreen mode Exit fullscreen mode

If you look to the structure will see a internal folder, in my projects i like to building my domain and services in the internal folder, when you name a package as internal, it can only be imported from its parent directory’s packages.

First we will define a domain folder and inside this folder we will create a folder to represent every domain that we will use in this project, in this case we will start a CRUD to a Book.

package book

type Book struct {
    Title       string
    Author      string
    NumberPages int
}

Enter fullscreen mode Exit fullscreen mode

This struct will represent our book model with information that will store inside our database.

It's a simple struct because i want to show you how to build a entire project, after understand the structure you can improve this domain like you want.

After that we will create our service layer, inside internal folder you can check the service folder, in this folder we will group all services necessary to build this project, you will see a lot of post's represent this layer like UseCase layer. If we decide to put any business rules in this project, the service layer is the place.

package service

import (
    "github.com/maaarkin/hero-api-golang/internal/domain/book"
)

type BookService interface {
    Save(book book.Book) (*book.Book, error)
    FindAll() (*[]book.Book, error)
    FindById(id uint64) (*book.Book, error)
    Delete(id uint64) error
    Update(book book.Book) error
}

type bookServiceImpl struct {
}

func NewBookService() BookService {
    return bookServiceImpl{}
}

func (bookServiceImpl) Save(book book.Book) (*book.Book, error) {
    return nil, nil
}

func (bookServiceImpl) FindAll() (*[]book.Book, error) {
    return nil, nil
}

func (bookServiceImpl) FindById(id uint64) (*book.Book, error) {
    return nil, nil
}

func (bookServiceImpl) Delete(id uint64) error {
    return nil
}

func (bookServiceImpl) Update(book book.Book) error {
    return nil
}

Enter fullscreen mode Exit fullscreen mode

In this moment we just create the service layer, you can see that we return nil in all services because we will conect this layer with the repository layer in the next post, but we will build right now to understand how we will inject this service layer in our handlers.

And we will do that through the DI Container, see what i like to do in my projects to represent this "container"...

In the internal folder you can check the container package and the code is very simple.

package container

import (
    "github.com/maaarkin/hero-api-golang/internal/service"
)

// TODO declare services/repositories/components, here
type Container struct {
    Services Services
}

type Services struct {
    BookService service.BookService
}

// Inject represent the starter of our IoC Container, here we will inject
// the necessary structs/functions that we need to build our project.
func Inject() Container {

    //init services
    bs := service.NewBookService()

    services := Services{
        BookService: bs,
    }

    return Container{
        Services: services,
    }
}
Enter fullscreen mode Exit fullscreen mode

The container.go file is the place that we will make our "New()" functions to start any service, repository or component that we want.

And this container we will invoke in our main.go and pass this container through the layers and we will inject the dependencies that are necessary to our project start.

after that, our main.go will looks like this.

package main

import (
    "github.com/maaarkin/hero-api-golang/cmd/http/handler"
    "github.com/maaarkin/hero-api-golang/internal/container"
)

func main() {

    di := container.Inject()
    //delegate to start our Http Server
    handler.StartServer(di)
}
Enter fullscreen mode Exit fullscreen mode

And in the router.go we change this functions

func initHandlers(di container.Container) handlers {
    return handlers{
                //here we will inject the service to the handler
        Book: NewBookHandler(di.Services.BookService),
    }
}

func StartServer(di container.Container) {
    //...
    handlers := initHandlers(di)

    //...
}
Enter fullscreen mode Exit fullscreen mode

If you see the initHandlers(...) function you will see that we pass the container and in the moment that we start our BookHandler we pass the service to the NewBookHandler(di.Services.BookService).

That way, in the handler we can access the BookService interface that represent the service layer.

In this moment we know how to create our domain and service layer and understand how to start our components and pass the information in the project.

In the next post we will improve our handler to make the CRUD functions and access the service layer.

Discussion (5)

Collapse
brunobandev profile image
Bruno Bandeira • Edited

Hey man, let me ask you something, in case we'd have a database connection, where should be the correct place to put the driver of connections? thanks

Collapse
maaarkin profile image
Marcos Filho Author

there's no "correct place", but i like to put in internal/db to be more easy to understand

Collapse
brunobandev profile image
Bruno Bandeira • Edited

Thanks bro, I am trying to follow your boilerplate to build something. One more question will you continue this series?

Thread Thread
maaarkin profile image
Marcos Filho Author

yes, i'm changed to another company, then i'm need some time to study the new stack. But i will finish this post

Thread Thread
brunobandev profile image
Bruno Bandeira

Cool, I'm waiting for. Good luck bro :)