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  ```