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:
- Project structure:
/internal
/repository
user_repository.go
/service
user_service.go
/model
user.go
/proto
user.proto
/cmd
main.go
- 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);
}
- User model (model/user.go):
type User struct {
ID string
Name string
Email string
}
- 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
}
- 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.