Sobes.tech
Middle+

Implement the BulkCreateUsers method based on the proto file: create project structure, business logic, repository, and SQL queries following SOLID principles.

sobes.tech AI

Answer from AI

To implement the BulkCreateUsers method in Go using protobuf and adhering to SOLID principles, the project can be structured as follows:

  1. Project structure:
/internal
  /repository
    user_repository.go
  /service
    user_service.go
  /model
    user.go
  /proto
    user.proto
/cmd
  main.go
  1. Proto file (user.proto):
syntax = "proto3";
package user;

message User {
  string id = 1;
  string name = 2;
  string email = 3;
}

message BulkCreateUsersRequest {
  repeated User users = 1;
}

message BulkCreateUsersResponse {
  int32 created_count = 1;
}

service UserService {
  rpc BulkCreateUsers(BulkCreateUsersRequest) returns (BulkCreateUsersResponse);
}
  1. User model (model/user.go):
type User struct {
  ID    string
  Name  string
  Email string
}
  1. Repository (repository/user_repository.go):
package repository

import (
  "context"
  "database/sql"
  "strings"
  "your_project/internal/model"
)

type UserRepository interface {
  BulkCreate(ctx context.Context, users []model.User) (int, error)
}

type userRepository struct {
  db *sql.DB
}

func NewUserRepository(db *sql.DB) UserRepository {
  return &userRepository{db: db}
}

func (r *userRepository) BulkCreate(ctx context.Context, users []model.User) (int, error) {
  if len(users) == 0 {
    return 0, nil
  }

  // Example SQL query for bulk insert
  var (
    valueStrings []string
    valueArgs    []interface{}
  )

  for i, user := range users {
    valueStrings = append(valueStrings, fmt.Sprintf("($%d, $%d, $%d)", i*3+1, i*3+2, i*3+3))
    valueArgs = append(valueArgs, user.ID, user.Name, user.Email)
  }

  stmt := fmt.Sprintf("INSERT INTO users (id, name, email) VALUES %s", strings.Join(valueStrings, ","))

  res, err := r.db.ExecContext(ctx, stmt, valueArgs...)
  if err != nil {
    return 0, err
  }

  count, err := res.RowsAffected()
  if err != nil {
    return 0, err
  }

  return int(count), nil
}
  1. Service (service/user_service.go):
package service

import (
  "context"
  "your_project/internal/model"
  "your_project/internal/repository"
)

type UserService interface {
  BulkCreateUsers(ctx context.Context, users []model.User) (int, error)
}

type userService struct {
  repo repository.UserRepository
}

func NewUserService(repo repository.UserRepository) UserService {
  return &userService{repo: repo}
}

func (s *userService) BulkCreateUsers(ctx context.Context, users []model.User) (int, error) {
  // Validation, business logic, etc.
  // For example, checking email uniqueness, filtering empty values, etc.

  return s.repo.BulkCreate(ctx, users)
}

This approach separates responsibilities: the repository handles database interactions, the service manages business logic, and the proto file defines the API contract. This aligns with SOLID principles, especially single responsibility and dependency inversion.