Let's Go پاسخ‌های مبتنی بر پایگاه داده (Database-Driven Responses) › طراحی مدل پایگاه داده (Designing a Database Model)
قبلی · فهرست · بعدی
فصل ۴.۵.

طراحی مدل پایگاه داده (Designing a Database Model)

قبل از اینکه بتوانیم از استخر اتصال پایگاه داده استفاده کنیم، باید یک مدل پایگاه داده (Database Model) طراحی کنیم که شامل تمام منطق برای تعامل با پایگاه داده باشد.

در Go، یک الگوی رایج این است که یک لایه دسترسی به داده (Data Access Layer) ایجاد کنیم که شامل تمام کد مربوط به پایگاه داده در یک بسته مستقل باشد.

اگر با اصطلاح مدل (Model) راحت نیستید، می‌توانید آن را به عنوان یک لایه سرویس (Service Layer) یا لایه دسترسی به داده (Data Access Layer) در نظر بگیرید. هر اصطلاحی که ترجیح می‌دهید، ایده این است که ما کد کار با MySQL را در یک پکیج جداگانه از بقیه برنامه کپسوله خواهیم کرد.

فعلاً، ما یک مدل پایگاه داده اسکلتی ایجاد می‌کنیم و آن را با مقداری داده آزمایشی برمی‌گردانیم. خیلی کار نخواهد کرد، اما می‌خواهم قبل از اینکه وارد جزئیات پرس‌وجوهای SQL شویم، الگو را توضیح دهم.

خوب به نظر می‌رسد؟ پس بیایید یک دایرکتوری جدید internal/models ایجاد کنیم که حاوی یک فایل snippets.go است:

$ mkdir -p internal/models
$ touch internal/models/snippets.go
04.05-01.png

بیایید فایل internal/models/snippets.go را باز کنیم و یک ساختار Snippet جدید برای نمایش داده‌های یک قطعه کد منفرد، به همراه یک نوع SnippetModel با متدهایی برای دسترسی و دستکاری قطعه‌های کد در پایگاه داده‌مان اضافه کنیم. به این صورت:

File: internal/models/snippets.go
package models

import (
    "database/sql"
    "time"
)

// Define a Snippet type to hold the data for an individual snippet. Notice how
// the fields of the struct correspond to the fields in our MySQL snippets
// table?
type Snippet struct {
    ID      int
    Title   string
    Content string
    Created time.Time
    Expires time.Time
}

// Define a SnippetModel type which wraps a sql.DB connection pool.
type SnippetModel struct {
    DB *sql.DB
}

// This will insert a new snippet into the database.
func (m *SnippetModel) Insert(title string, content string, expires int) (int, error) {
    return 0, nil
}

// This will return a specific snippet based on its id.
func (m *SnippetModel) Get(id int) (Snippet, error) {
    return Snippet{}, nil
}

// This will return the 10 most recently created snippets.
func (m *SnippetModel) Latest() ([]Snippet, error) {
    return nil, nil
}

استفاده از SnippetModel (Using the SnippetModel)

برای استفاده از این مدل در هندلرهایمان، نیاز داریم یک ساختار SnippetModel جدید در تابع main() خود ایجاد کنیم و سپس آن را به عنوان یک وابستگی از طریق ساختار application تزریق کنیم - درست مانند کاری که با سایر وابستگی‌هایمان انجام داده‌ایم.

به این صورت:

File: cmd/web/main.go
package main

import (
    "database/sql"
    "flag"
    "log/slog"
    "net/http"
    "os"

    // Import the models package that we just created. You need to prefix this with
    // whatever module path you set up back in chapter 02.01 (Project Setup and Creating
    // a Module) so that the import statement looks like this:
    // "{your-module-path}/internal/models". If you can't remember what module path you 
    // used, you can find it at the top of the go.mod file.
    "snippetbox.letsgofa.net/internal/models" 

    _ "github.com/go-sql-driver/mysql"
)

// Add a snippets field to the application struct. This will allow us to
// make the SnippetModel object available to our handlers.
type application struct {
    logger   *slog.Logger
    snippets *models.SnippetModel
}

func main() {
    addr := flag.String("addr", ":4000", "HTTP network address")
    dsn := flag.String("dsn", "web:pass@/snippetbox?parseTime=true", "MySQL data source name")
    flag.Parse()

    logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

    db, err := openDB(*dsn)
    if err != nil {
        logger.Error(err.Error())
        os.Exit(1)
    }
    defer db.Close()

    // Initialize a models.SnippetModel instance containing the connection pool
    // and add it to the application dependencies.
    app := &application{
        logger:   logger,
        snippets: &models.SnippetModel{DB: db},
    }

    logger.Info("starting server", "addr", *addr)

    err = http.ListenAndServe(*addr, app.routes())
    logger.Error(err.Error())
    os.Exit(1)
}

...

اطلاعات تکمیلی (Additional Information)

مزایای این ساختار (Benefits of this Structure)

اگر یک قدم به عقب بردارید، ممکن است بتوانید چند مزیت از راه‌اندازی پروژه به این روش را ببینید:

واژه‌نامه اصطلاحات فنی

اصطلاح فارسی معادل انگلیسی توضیح
مدل پایگاه داده Database Model ساختاری که نحوه ذخیره‌سازی و دسترسی به داده‌ها را تعریف می‌کند
لایه دسترسی به داده Data Access Layer بخشی از برنامه که مسئول تعامل با پایگاه داده است
بسته مستقل Independent Package بسته‌ای که می‌تواند به صورت مستقل از سایر بخش‌های برنامه استفاده شود
ساختار داده Data Structure روشی برای سازماندهی و ذخیره داده‌ها در برنامه
متدهای دسترسی Access Methods توابعی که برای خواندن و نوشتن داده‌ها در پایگاه داده استفاده می‌شوند
منطق کسب و کار Business Logic قوانین و عملیاتی که داده‌ها را پردازش می‌کنند
جداسازی مسئولیت‌ها Separation of Concerns اصل طراحی که هر بخش از کد باید وظیفه مشخصی داشته باشد
واسط برنامه‌نویسی Programming Interface مجموعه‌ای از متدها برای تعامل با یک بخش از برنامه
مدیریت خطا Error Handling مکانیزم‌های کنترل و پاسخ به خطاها در برنامه
کپسوله‌سازی Encapsulation پنهان کردن جزئیات پیاده‌سازی و ارائه یک رابط ساده