(GO) Một vài lưu ý
1. Denpendency Injection
Khi phân tầng, Handler gọi xuống tầng Usecase qua một hàm, tạm gọi GetUser, tại tầng này GetUser gọi xuống Repo/Storage thông qua Contructor của một struct đã inject Interface X. Dĩ nhiên X chứa phương thức xử lý dữ liệu cho tầng Repo.
Tầng business có thể gọi Repo thông qua biz.store.GetUser(). Lúc này Go sẽ tìm có method nào đã implement đúng phương thức của interface X trùng tên, trùng chữ ký và thực hiện nó.
2. Hàm trả về hàm
Là closure. Ứng dụng trong viết Handler, Middleware...
Khi có Request gửi đến, GIN/ECHO... gọi hàm xử lý, ví dụ func (ctx *gin.Context) . Hàm này không thể truyền DB vào được, nên viết handler dưới dạng Closure để truyền DB. Việc giữ được trạng thái riêng của Closure giúp cho router có thể tạo riêng handler có nhiều DB khác nhau mà không ảnh hưởng. Tương tự với việc xử lý channel trong Routine và tùy biến Middleware.
3. Hàm nhận và trả về Interface.
a. Hàm nhận vào 1 Interface:
Hàm nhận vào interface thì nó nhận vào các phương thức từ các struct đã implement nó trước đó. Tùy vào đối tượng được tạo ra từ struct nào thì phương thức tương ứng được gọi.
b. Hàm trả về một interface:
Trả về địa chỉ của struct đã implement interface đó. Nếu có nhiều struct cùng implement thì phải chọn struct nào ta muốn trả về kết quả.
Ứng dụng trong việc xây dựng một file Provider cho việc xác thực.
Có nhiều kiểu xác thực, Google Auth, Facebook Auth, JWT. Provider thường có 3 phương thức gồm Generate, Validate, SecretKey. Nên tạo Provider là các interface tách biệt như vậy giúp cho việc mở rộng xác thực mà không phải sửa nhiều code. Khi muốn tạo xác thực mới, chỉ cần cho implement được 3 phương thức trên là được.
Có một lưu ý là làm theo cách trên thi hàm Generate() nên trả về interface token trong Provider. Khi một hàm trả về một interface thì kết quả có thể tùy ý trả về địa chỉ một struct nào đó đã implement interface đó. Tất nhiên có thể lựa chọn trả về trực tiếp Struct Token , nhưng việc thông qua interface giúp code linh hoạt, giảm phụ thuộc.
4. Cẩn trọng với việc khai báo các loại biến tham chiếu (Type reference) như Map, Slice...
Khai báo bằng từ khóa Var xong mà gán giá trị vào sẽ bị panic. Đối với các biến tham chiếu, khi khai báo bằng Var sẽ bị gán nil, việc dereference giá trị nil sẽ gây lỗi panic. Tốt nhất là sử dụng make() để vừa khai báo vửa khởi tạo vùng nhớ để khi thao tác không bị lỗi.
Struct không phải loại tham chiếu. Các biến nguyên thủy nhu int, string.. khi khai báo đã được cấp phát vùng nhớ và có giá trị mặc đinh rồi nên việc thao tác gán giá trị không gây ra lỗi
5. Connection Pool
Một kết nối sẽ tốn rất nhiều chi phí (TCP >3 ways handshake, đóng kết nối cũng phải làm 3 bước này).
Dùng 3 hàm để quản lý gồm
- max kết nối (khoảng 100): Giả sử có 1000 request thì chỉ cần 100 kết nối, nếu mở 1000 kết nối Database ko chịu nổi, có thể báo too many connections
- Quản lý thời gian: Sau khoảng 1 tiếng thì đóng 90 kết nối
- Giữ 10 kết nối để có thể có request
Ví dụ về GORM:
sqlDB, err := db.DB()
// SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns sets the maximum number of open connections to the database.
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
sqlDB.SetConnMaxLifetime(time.Hour)
6. Strong Entity vs Week Entity
- Strong Entity là bảng chứa nhiều khóa ngoại
- Week Entity thường là bản trung gian trong quan hệ n-n như orderDetails
- Week Entity không hoặc chứa ít khóa ngoại như Status, Category, Country
- Strong Entity chứa nhiều khóa ngoại nhưng, là bảng trung tâm nghiệp vụ như Product, Order….
Comments
Post a Comment