github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/docs/v2/design/framework/model.md (about) 1 # Model 2 3 Model is an interface for data modelling. It's akin to client and server as a building block for services development. 4 Where the server handles queries and the client makes queries, the model is used for storing and accessing data. 5 6 ## Overview 7 8 The model builds on the store interface much like client/server build on transport/broker. The model is much like 9 rails activerecord and provides a simple crud layer for accessing the underlying data store, allowing the developer 10 to focus on their data types rather than raw storage. 11 12 ## Design 13 14 Here's the proposed design for the model to be added as go-micro/model 15 16 ### Interface 17 18 ``` 19 type Model interface { 20 // Initialise options 21 Init(...Option) error 22 // Retrieve options 23 Options() Options 24 // Register a type 25 Register(v interface{}) error 26 // Create a record 27 Create(v interface, ...CreateOption) error 28 // Read a record into v 29 Read(v interface{}, ...ReadOption) error 30 // Update a record 31 Update(v interface{}, ...UpdateOption) error 32 // Delete a record 33 Delete(v interface{}, ...DeleteOption) error 34 // Model implementation 35 String() string 36 } 37 ``` 38 39 Additionally there is the potential to create an `Entity` value aligned with Message/Request in Client/Server that extracts the common 40 values required for any entity to be stored. 41 42 ``` 43 type Entity interface { 44 // Id of the record 45 Id() string 46 // Type of entity e.g User 47 Type() string 48 // Associated value 49 Value() interface{} 50 } 51 ``` 52 53 This would expect the model to accept Entity in Create/Update/Delete rather than just an interface type. Similarly how client.Call 54 accepts a client.Request or client.Publish accepts a client.Message. 55 56 ### Storage 57 58 The model would accept go-micro/store.Store as an option for which it would use as storage. This allows the model to be agnostic 59 of any storage type and keeps it pluggable much like the rest of the go-micro packages. 60 61 ``` 62 // passed as an option 63 m := model.NewModel( 64 model.Store(store), 65 ) 66 ``` 67 68 ### Encoding 69 70 The model would also accept a codec which would allow simple marshaling/unmarshalling of data types much like the client/server. 71 We quite simply pass codec.Marshaler which has the methods Marshal/Unmarshal to the model. The preference is to use the proto 72 codec by default so that we have efficient serialisation of data especially with large data types. 73 74 ``` 75 m := model.NewModel( 76 mode.Codec(codec), 77 ) 78 ``` 79 80 ## Usage 81 82 Usage would be as follows 83 84 Define your types, ideally in proto 85 86 ``` 87 message User { 88 string name = 1; 89 string email = 2; 90 } 91 ``` 92 93 Then register this against a model 94 95 ``` 96 // create a new model 97 m := mode.NewModel() 98 99 // register the User with the model 100 m.Register(new(User)) 101 102 // register with index 103 m.Register(new(User), model.AddIndex("name")) 104 ``` 105 106 This should initialise the model to use the `users` table and map any necessary fields for indexing. Fields which need to be indexed 107 are mapped as metadata in the store.Record. An alternative strategy would be to index everything or index string and int fields 108 automatically. 109 110 111 Once you have the model the access would be as follows 112 113 ``` 114 // create a record 115 m.Create(&User{Name: "john"}) 116 117 // update a record 118 m.Update(&User{Name: "john", Email: "john@example.com"}) 119 ``` 120 121 Considering we may need to store with a primary key/id the alternative form would be 122 123 ``` 124 entity := m.NewEntity(&User{Name: "john"}) 125 126 // create the new entity 127 m.Create(entity) 128 ``` 129 130 ## Proto generation 131 132 With proto code generation this would evolve to the point where crud is generated for you much like for client/server 133 134 Assuming the following proto 135 136 ``` 137 service Users { 138 ...standard rpc 139 } 140 141 message User { 142 string name = 1; 143 string email = 2; 144 } 145 ``` 146 147 Then registering and using it with a service 148 ``` 149 // create a new service 150 service := micro.NewService() 151 152 // create a new user model 153 userModel := pb.NewUsersModel(service.Model()) 154 155 // assuming a request comes in to update email the model handles updating the single field 156 userModel.UpdateEmail(req.User) 157 ``` 158 159 The proto generated code generation might look as follows 160 161 ``` 162 func NewUsersModel(m model.Model) (UsersModel, error) { 163 if err := m.Register(new(User)); err != nil { 164 return nil, err 165 } 166 return &usersModel{m} 167 } 168 169 type usersModel struct { 170 model.Model 171 } 172 173 func (u *usersModel) UpdateEmail(user *User) error { 174 return m.Update(user, model.UpdateField("email", user.Email)) 175 } 176 ```