From b00470a76c6ff17cf2079a2eebb2bb7c4fae66b3 Mon Sep 17 00:00:00 2001 From: Janishar Ali Date: Mon, 1 Jul 2024 03:26:40 +0530 Subject: [PATCH] add micro service arch --- arch/micro/context.go | 19 +++++++++++ arch/micro/controller.go | 24 ++++++++++++++ arch/micro/interfaces.go | 34 ++++++++++++++++++++ arch/micro/message.go | 19 +++++++++++ arch/micro/nats.go | 40 +++++++++++++++++++++++ arch/micro/request.go | 37 ++++++++++++++++++++++ arch/micro/router.go | 65 ++++++++++++++++++++++++++++++++++++++ arch/network/interfaces.go | 8 +++-- go.mod | 3 ++ go.sum | 6 ++++ 10 files changed, 253 insertions(+), 2 deletions(-) create mode 100644 arch/micro/context.go create mode 100644 arch/micro/controller.go create mode 100644 arch/micro/interfaces.go create mode 100755 arch/micro/message.go create mode 100644 arch/micro/nats.go create mode 100644 arch/micro/request.go create mode 100644 arch/micro/router.go diff --git a/arch/micro/context.go b/arch/micro/context.go new file mode 100644 index 0000000..ddf467c --- /dev/null +++ b/arch/micro/context.go @@ -0,0 +1,19 @@ +package micro + +import ( + "context" +) + +type NatsContext struct { + context.Context + Client *NatsClient + Subject string +} + +func NewNatsContext(client *NatsClient, baseSub string) *NatsContext { + return &NatsContext{ + Context: context.Background(), + Client: client, + Subject: baseSub, + } +} diff --git a/arch/micro/controller.go b/arch/micro/controller.go new file mode 100644 index 0000000..2f14d02 --- /dev/null +++ b/arch/micro/controller.go @@ -0,0 +1,24 @@ +package micro + +import ( + "github.com/unusualcodeorg/goserve/arch/network" +) + +type baseController struct { + network.BaseController + natsCtx *NatsContext +} + +func NewBaseController(basePath string, authProvider network.AuthenticationProvider, authorizeProvider network.AuthorizationProvider) BaseController { + return &baseController{ + BaseController: network.NewBaseController(basePath, authProvider, authorizeProvider), + } +} + +func (c *baseController) SetNatsContext(ctx *NatsContext) { + c.natsCtx = ctx +} + +func (c *baseController) NatsContext() *NatsContext { + return c.natsCtx +} diff --git a/arch/micro/interfaces.go b/arch/micro/interfaces.go new file mode 100644 index 0000000..ec48cca --- /dev/null +++ b/arch/micro/interfaces.go @@ -0,0 +1,34 @@ +package micro + +import ( + "github.com/nats-io/nats.go/micro" + "github.com/unusualcodeorg/goserve/arch/network" +) + +type NatsGroup = micro.Group +type NatsHandlerFunc = micro.HandlerFunc +type NatsRequest = micro.Request + +type Config struct { + NatsUrl string + NatsServiceName string + NatsServiceVersion string +} + +type BaseController interface { + network.BaseController + SetNatsContext(ctx *NatsContext) + NatsContext() *NatsContext +} + +type Controller interface { + BaseController + MountNats(group NatsGroup) +} + +type Router interface { + network.BaseRouter + NatsClient() *NatsClient + Disconnect() + LoadControllers(controllers []Controller) +} diff --git a/arch/micro/message.go b/arch/micro/message.go new file mode 100755 index 0000000..30d7771 --- /dev/null +++ b/arch/micro/message.go @@ -0,0 +1,19 @@ +package micro + +type Message[T any] struct { + Data *T `json:"data,omitempty"` + Error *string `json:"error,omitempty"` +} + +func NewMessage[T any](data *T, err error) *Message[T] { + var e *string + if err != nil { + er := err.Error() + e = &er + } + + return &Message[T]{ + Data: data, + Error: e, + } +} diff --git a/arch/micro/nats.go b/arch/micro/nats.go new file mode 100644 index 0000000..adee737 --- /dev/null +++ b/arch/micro/nats.go @@ -0,0 +1,40 @@ +package micro + +import ( + "fmt" + "time" + + "github.com/nats-io/nats.go" + "github.com/nats-io/nats.go/micro" +) + +type NatsClient struct { + Conn *nats.Conn + Service micro.Service + Timeout time.Duration +} + +func NewNatsClient(config Config) *NatsClient { + fmt.Println("connecting to nats..") + + nc, err := nats.Connect(config.NatsUrl) + if err != nil { + panic(err) + } + + srv, err := micro.AddService(nc, micro.Config{ + Name: config.NatsServiceName, + Version: config.NatsServiceVersion, + }) + if err != nil { + panic(err) + } + + fmt.Println("connected to nats..") + + return &NatsClient{ + Conn: nc, + Service: srv, + Timeout: nats.DefaultTimeout, + } +} diff --git a/arch/micro/request.go b/arch/micro/request.go new file mode 100644 index 0000000..0dfd1eb --- /dev/null +++ b/arch/micro/request.go @@ -0,0 +1,37 @@ +package micro + +import ( + "encoding/json" + "errors" + "fmt" +) + +func Respond[T any](req NatsRequest, data *T, err error) { + req.RespondJSON(NewMessage(data, err)) +} + +func Request[T any, V any](ctx *NatsContext, sub string, send *T, receive *V) (*V, error) { + sendMsg := NewMessage(send, nil) + sendPayload, err := json.Marshal(sendMsg) + if err != nil { + return nil, err + } + + subject := fmt.Sprintf(`%s.%s`, ctx.Subject, sub) + msg, err := ctx.Client.Conn.Request(subject, sendPayload, ctx.Client.Timeout) + if err != nil { + return nil, err + } + + var receiveMsg Message[V] + err = json.Unmarshal(msg.Data, &receiveMsg) + if err != nil { + return nil, err + } + + if receiveMsg.Error != nil { + err = errors.New(*receiveMsg.Error) + } + + return receiveMsg.Data, err +} diff --git a/arch/micro/router.go b/arch/micro/router.go new file mode 100644 index 0000000..b154ee1 --- /dev/null +++ b/arch/micro/router.go @@ -0,0 +1,65 @@ +package micro + +import ( + "fmt" + "strings" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + "github.com/unusualcodeorg/goserve/arch/network" +) + +type router struct { + netRouter network.Router + natsClient *NatsClient +} + +func NewRouter(mode string, config Config) Router { + natsClient := NewNatsClient(config) + return &router{ + netRouter: network.NewRouter(mode), + natsClient: natsClient, + } +} + +func (r *router) GetEngine() *gin.Engine { + return r.netRouter.GetEngine() +} + +func (r *router) NatsClient() *NatsClient { + return r.natsClient +} + +func (r *router) Disconnect() { + r.natsClient.Conn.Close() +} + +func (r *router) LoadRootMiddlewares(middlewares []network.RootMiddleware) { + r.netRouter.LoadRootMiddlewares(middlewares) +} + +func (r *router) LoadControllers(controllers []Controller) { + nc := make([]network.Controller, len(controllers)) + for i, c := range controllers { + nc[i] = c.(network.Controller) + } + r.netRouter.LoadControllers(nc) + + for _, c := range controllers { + baseSub := fmt.Sprintf(`%s.%s`, r.natsClient.Service.Info().Name, strings.ReplaceAll(c.Path(), "/", "")) + + ctx := NewNatsContext(r.natsClient, baseSub) + c.SetNatsContext(ctx) + + ng := r.natsClient.Service.AddGroup(baseSub) + c.MountNats(ng) + } +} + +func (r *router) Start(ip string, port uint16) { + r.netRouter.Start(ip, port) +} + +func (r *router) RegisterValidationParsers(tagNameFunc validator.TagNameFunc) { + r.netRouter.RegisterValidationParsers(tagNameFunc) +} diff --git a/arch/network/interfaces.go b/arch/network/interfaces.go index d1984ec..3e04a99 100644 --- a/arch/network/interfaces.go +++ b/arch/network/interfaces.go @@ -101,14 +101,18 @@ type ParamNMiddlewareProvider[T any] interface { type AuthenticationProvider Param0MiddlewareProvider type AuthorizationProvider ParamNMiddlewareProvider[string] -type Router interface { +type BaseRouter interface { GetEngine() *gin.Engine RegisterValidationParsers(tagNameFunc validator.TagNameFunc) - LoadControllers(controllers []Controller) LoadRootMiddlewares(middlewares []RootMiddleware) Start(ip string, port uint16) } +type Router interface { + BaseRouter + LoadControllers(controllers []Controller) +} + type Module[T any] interface { GetInstance() *T RootMiddlewares() []RootMiddleware diff --git a/go.mod b/go.mod index e2b5485..c5789d9 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/go-playground/validator/v10 v10.22.0 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/jinzhu/copier v0.4.0 + github.com/nats-io/nats.go v1.35.0 github.com/redis/go-redis/v9 v9.5.3 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 @@ -40,6 +41,8 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.7.1 // indirect + github.com/nats-io/nkeys v0.4.7 // indirect + github.com/nats-io/nuid v1.0.1 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sagikazarmark/locafero v0.6.0 // indirect diff --git a/go.sum b/go.sum index 2addf82..9728e75 100644 --- a/go.sum +++ b/go.sum @@ -76,6 +76,12 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/nats-io/nats.go v1.35.0 h1:XFNqNM7v5B+MQMKqVGAyHwYhyKb48jrenXNxIU20ULk= +github.com/nats-io/nats.go v1.35.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= +github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=