راهاندازی پروژه و ساختار اسکلت
کار را با ایجاد یک دایرکتوری به نام greenlight شروع میکنیم تا نقش «خانه» سطح بالای این پروژه را داشته باشد. من دایرکتوری پروژه خودم را در $HOME/Projects/greenlight ایجاد میکنم، اما اگر خواستید میتوانید محل دیگری را انتخاب کنید.
$ mkdir -p $HOME/Projects/greenlight
سپس وارد این دایرکتوری شوید و از دستور go mod init استفاده کنید تا modules را برای پروژه فعال کنید.
هنگام اجرای این دستور، باید یک module path مشخص کنید. این مقدار در اصل یک شناسه یکتا برای پروژه شماست. در این کتاب من از greenlight.alexedwards.net به عنوان module path استفاده میکنم، اما اگر همراه کتاب پیش میروید بهتر است آن را با مقداری جایگزین کنید که مخصوص خودتان باشد.
$ cd $HOME/Projects/greenlight $ go mod init greenlight.alexedwards.net go: creating new go.mod: module greenlight.alexedwards.net
در این مرحله میبینید که یک فایل go.mod در ریشه دایرکتوری پروژه شما ایجاد شده است. اگر آن را باز کنید، باید چیزی شبیه این ببینید:
module greenlight.alexedwards.net go 1.25.0
در کتاب اول Let’s Go درباره modules با جزئیات صحبت کردیم، اما برای یادآوری سریع، نکات اصلی را اینجا مرور میکنیم.
- وقتی یک فایل معتبر
go.modدر ریشه دایرکتوری پروژه شما وجود داشته باشد، پروژه شما یک module است. - وقتی داخل دایرکتوری پروژه خود کار میکنید و یک وابستگی را با
go getدانلود میکنید، نسخه دقیق آن وابستگی در فایلgo.modثبت میشود. چون نسخه دقیق مشخص است، اطمینان از buildهای تکرارپذیر روی ماشینها و محیطهای مختلف بسیار آسانتر میشود. - وقتی کد پروژه را اجرا یا build میکنید، Go از همان وابستگیهای دقیقی استفاده میکند که در فایل
go.modفهرست شدهاند. اگر وابستگیهای لازم از قبل روی ماشین محلی شما وجود نداشته باشند، Go آنها را به صورت خودکار برایتان دانلود میکند؛ همراه با همه وابستگیهای بازگشتی آنها. - فایل
go.modهمچنین module path را تعریف میکند که در مورد منgreenlight.alexedwards.netاست. این مقدار در اصل شناسهای است که به عنوان root import path برای پکیجهای پروژه شما استفاده خواهد شد. - بهتر است module path را برای خودتان و پروژهتان یکتا انتخاب کنید. یک قرارداد رایج در جامعه Go این است که آن را بر اساس URLای بسازید که مالک آن هستید.
ایجاد ساختار دایرکتوری اسکلت
خوب، حالا که دایرکتوری پروژه ایجاد شده و فایل go.mod را داریم، میتوانید دستورهای زیر را اجرا کنید تا یک ساختار اسکلت سطح بالا برای پروژه ایجاد شود:
$ mkdir -p bin cmd/api internal migrations remote $ touch Makefile $ touch cmd/api/main.go
در این مرحله دایرکتوری پروژه شما باید دقیقا به این شکل باشد:
. ├── bin ├── cmd │ └── api │ └── main.go ├── internal ├── migrations ├── remote ├── go.mod └── Makefile
بیایید کمی درباره این فایلها و پوشهها صحبت کنیم و توضیح بدهیم که در پروژه نهایی ما چه نقشی خواهند داشت.
- دایرکتوری
binشامل باینریهای کامپایلشده برنامه ما خواهد بود که آماده استقرار روی سرور production هستند. - دایرکتوری
cmd/apiشامل کدهای اختصاصی برنامه API ما، یعنی Greenlight، خواهد بود. این شامل کد اجرای سرور، خواندن و نوشتن درخواستهای HTTP و مدیریت احراز هویت میشود. - دایرکتوری
internalشامل پکیجهای کمکی مختلفی خواهد بود که API ما از آنها استفاده میکند. این دایرکتوری کدهای مربوط به تعامل با پایگاه داده، اعتبارسنجی داده، ارسال ایمیل و موارد مشابه را در خود دارد. به طور کلی، هر کدی که اختصاصی خود برنامه نباشد و امکان استفاده دوباره از آن وجود داشته باشد، اینجا قرار میگیرد. کد Go ما زیرcmd/apiپکیجهای داخل دایرکتوریinternalرا import میکند، اما هرگز برعکس. - دایرکتوری
migrationsشامل فایلهای migration مربوط به SQL برای پایگاه داده ما خواهد بود. - دایرکتوری
remoteشامل فایلهای پیکربندی و اسکریپتهای راهاندازی برای سرور production ما خواهد بود. - فایل
go.modوابستگیهای پروژه، نسخهها و module path را اعلام میکند. - فایل
Makefileشامل recipeهایی برای خودکارسازی کارهای مدیریتی رایج خواهد بود؛ مثل audit کردن کد Go، ساخت باینریها و اجرای migrationهای پایگاه داده.
مهم است اشاره کنیم که نام دایرکتوری internal در Go معنا و رفتار ویژهای دارد: هر پکیجی که زیر این دایرکتوری قرار بگیرد، فقط میتواند توسط کدی import شود که داخل والد دایرکتوری internal قرار دارد. در مورد ما، این یعنی هر پکیجی که در internal قرار بگیرد فقط توسط کد داخل دایرکتوری پروژه greenlight قابل import است.
یا اگر از زاویه دیگر نگاه کنیم، یعنی هر پکیجی زیر internal نمیتواند توسط کدی خارج از پروژه ما import شود.
این مفید است، چون مانع میشود codebaseهای دیگر پکیجهای داخل دایرکتوری internal ما را import کنند و به آنها وابسته شوند؛ پکیجهایی که ممکن است نسخهبندی نشده یا پشتیبانینشده باشند، حتی اگر کد پروژه در جایی مثل GitHub به صورت عمومی در دسترس باشد.
Hello world!
قبل از ادامه، بیایید سریع بررسی کنیم که همه چیز درست راهاندازی شده است. فایل cmd/api/main.go را در ویرایشگر متن خود باز کنید و کد زیر را به آن اضافه کنید:
package main import "fmt" func main() { fmt.Println("Hello world!") }
این فایل را ذخیره کنید، سپس در ترمینال از دستور go run استفاده کنید تا کد داخل پکیج cmd/api کامپایل و اجرا شود. اگر همه چیز درست باشد، خروجی زیر را خواهید دید:
$ go run ./cmd/api Hello world!