Информация об изменениях

Сообщение Re: Практики разбивки проекта на пакеты от 02.01.2024 8:30

Изменено 02.01.2024 8:31 Doom100500

Re: Практики разбивки проекта на пакеты
Здравствуйте, a9000, Вы писали:

A>Добрый день! Есть проект на Go, проект разрастается, и стало неудобно когда все файлы в IDE в одном длинном списке. Хочется разбить на папки.

A>А, насколько я понимаю, это также подразумевает разбивку на пакеты (или нет?).

Да, это подразумевает разбиение на пакеты, и, как следствие, заставляет задуматься о публичных интерфейсах, доступных снаружи, приватной имплементации. Что, в свою очередь, приведёт к тому, что публичные интерфейсы можно будет мокать в тестах. Одни прелести вобщем.

A>Сейчас есть main, содержащий глобальную структуру Application, в которой хранятся всякие общие для всего проекта сущности

A>Объект соединения с БД
A>Объект с разными настройками программы, читаемыми из конфига
A>Объект соединения с удаленным сервисом, предоставляющим данные по API
A>Объект собственного веб-сервера
A>Объект прокси
A>и т.п.

A>Есть группа файлов, которая занимается только работой с БД (выполняет запросы и возвращает массивы с результатами)


A>Есть группа файлов, которая занимается запросами к удаленному серверу с помощью его API и складыванием результатов в БД


A>Есть группа файлов, которая занимается формированием собственного веб-интерфейса; она также использует объект БД


A>Есть файлы, содержащие общие вспомогательные функции


A>Хочется все это разделить. Но к примеру база должна быть доступна всем "пакетам". Передавать ее каждый раз как аргумент?


// В поддиректории svc где-то в иерархии 
// пишу без IDE, могут быть ошибки
package users

import (
    "context"
    "myprject/internal/database"
)

func New(db database.MyDatabase) Svc {
    return &svc{db: db}
}

type Svc interface {
    UserInfo(ctx context.Context, id string) (*User, error)
}

type svc struct {
    db database.MyDatabase
// .....
}

func (s *svc) UserInfo(ctx context.Context, id string) (*User, error) {
    u, err := s.db.GetUser(ctx, id)
    if err != nil {
        return nil, err
    }
    return s.userFromModel(u)
}

//где-то в поддиректории database:

package database

import (
    "context"
    "myprject/internal/model"
)

func New(connectionString string, .......) (MyDatabase, error) {
    // ......
    return &db{/*......*/}, nil
}

type MyDatabase interface {
    GetUser() *model.User
}

type db struct {
    // 
}

// где-то в начале иерархии

package main

import (
    "context"
    "log"
    "myprject/internal/database"
    "myprject/internal/services/users"
)

func main() {
    db, err := database.New(config.connectionString, ......)
    if err != nil {
        log.Fatal(err)
    }
    usersSvc := users.New(db)
 
    u, err := usersSvc.UserInfo(context.TODO(), uid)
    if err != nil {
         log.Fatal(err)
    }
    log.Println(u.String())
}
Re: Практики разбивки проекта на пакеты
Здравствуйте, a9000, Вы писали:

A>Добрый день! Есть проект на Go, проект разрастается, и стало неудобно когда все файлы в IDE в одном длинном списке. Хочется разбить на папки.

A>А, насколько я понимаю, это также подразумевает разбивку на пакеты (или нет?).

Да, это подразумевает разбиение на пакеты, и, как следствие, заставляет задуматься о публичных интерфейсах, доступных снаружи, приватной имплементации. Что, в свою очередь, приведёт к тому, что публичные интерфейсы можно будет мокать в тестах. Одни прелести вобщем.

A>Сейчас есть main, содержащий глобальную структуру Application, в которой хранятся всякие общие для всего проекта сущности

A>Объект соединения с БД
A>Объект с разными настройками программы, читаемыми из конфига
A>Объект соединения с удаленным сервисом, предоставляющим данные по API
A>Объект собственного веб-сервера
A>Объект прокси
A>и т.п.

A>Есть группа файлов, которая занимается только работой с БД (выполняет запросы и возвращает массивы с результатами)


A>Есть группа файлов, которая занимается запросами к удаленному серверу с помощью его API и складыванием результатов в БД


A>Есть группа файлов, которая занимается формированием собственного веб-интерфейса; она также использует объект БД


A>Есть файлы, содержащие общие вспомогательные функции


A>Хочется все это разделить. Но к примеру база должна быть доступна всем "пакетам". Передавать ее каждый раз как аргумент?


// В поддиректории svc где-то в иерархии 
// пишу без IDE, могут быть ошибки
package users

import (
    "context"
    "myprject/internal/database"
)

func New(db database.MyDatabase) Svc {
    return &svc{db: db}
}

type Svc interface {
    UserInfo(ctx context.Context, id string) (*User, error)
}

type svc struct {
    db database.MyDatabase
// .....
}

func (s *svc) UserInfo(ctx context.Context, id string) (*User, error) {
    u, err := s.db.GetUser(ctx, id)
    if err != nil {
        return nil, err
    }
    return s.userFromModel(u)
}

//где-то в поддиректории database:

package database

import (
    "context"
    "myprject/internal/model"
)

func New(connectionString string, .......) (MyDatabase, error) {
    // ......
    return &db{/*......*/}, nil
}

type MyDatabase interface {
    GetUser() (*model.User, error)
}

type db struct {
    // 
}

// где-то в начале иерархии

package main

import (
    "context"
    "log"
    "myprject/internal/database"
    "myprject/internal/services/users"
)

func main() {
    db, err := database.New(config.connectionString, ......)
    if err != nil {
        log.Fatal(err)
    }
    usersSvc := users.New(db)
 
    u, err := usersSvc.UserInfo(context.TODO(), uid)
    if err != nil {
         log.Fatal(err)
    }
    log.Println(u.String())
}