为用户可以编辑的页面设计数据存储架构(例如Wikipedia / stackoverflow页面)

The idea is to design a table/entity that contains some basic info, as well as a Markdown-Content field that would allow users to easily create tables and such.

I'm thinking something like this:

type Tournament struct {
  ID in64 `datastore:"-"`
  MDContent []byte `datastore:",noindex"`
  Name string
  URL string
  DateCreated int64
  CreatedBy string
  DateUpdated int64
  UpdatedBy string
  ApprovalStatus int64 // 0=to be decided, 1=approved, 2=rejected, 3=discontinued
  ApprovalBy string
}

My problem is figuring out how to update it. The ID field will also be used as the URL path, e.g. if an entity has ID 7 then it will be displayed on example.com/tournament/7.

I believe this eliminates the possibility of simply creating a new entity with updated data, and then set the ApprovalStatus=3 on the previous entity, because if you do as such then the example.com/tournament/7 URL will no longer request the correct ID.

I also don't like the idea of creating my own unique ID because I think it would be great to simply take advantage of the Datastore ID generation (which also makes it easy to get the correct entity based on URL); I considered creating a new entity/table that would keep track of revisions but I'm not sure how efficient all of this is, so I was hoping some expert might be able to give some advice.


Update related to @mkopriva solution:

If you do it this way, then it's necessary to include a TournamentID field inside the TournamentEdit entity struct I think?

type TournamentEdit struct {
    ID             in64   `datastore:"-"`
    TournamentID   int64
    MDContent      []byte `datastore:",noindex"`
    DateCreated    int64
    CreatedBy      string
    ApprovalStatus int64 // 0=to be decided, 1=approved, 2=rejected, 3=discontinued
    ApprovalBy     string
}

And then the retrieve function could look like this:

func (db *datastoreDB) GetTournamentByKeyID(ctx context.Context, keyID int64) (*Tournament, error) {
  key := datastore.IDKey("Tournament", keyID, nil)
  var tournamnet Tournament
  err := db.client.Get(ctx, key, &tournament)
  // err checking
  tournament.ID = key.ID

  var edits TournamentEdits
  query := datastore.NewQuery("TournamentEdit")
  query = query.Filter("TournamentID =", tournament.ID)
  query = query.Filter("ApprovalStatus =", 1)
  if _, err := db.client.GetAll(ctx, query, &edits); err != nil {
    //err checking
  }
  tournament.Edits = edits // I guess this is wrong way to do it?
  return &tournament, nil
}

Would this work?

One thing you could do is to simply create a new entity that would represent the edit of a tournament. By the way, I'm not a datastore user so I'm not sure if this is how you would model the entities but the general idea is the same for most, if not all, databases:

type Tournament struct {
    ID          in64   `datastore:"-"`
    MDContent   []byte `datastore:",noindex"`
    Name        string
    URL         string
    DateCreated int64
    CreatedBy   string
    DateUpdated int64
    UpdatedBy   string
    Edits       []TournamentEdit
}

type TournamentEdit struct {
    ID             in64   `datastore:"-"`
    MDContent      []byte `datastore:",noindex"`
    DateCreated    int64
    CreatedBy      string
    ApprovalStatus int64 // 0=to be decided, 1=approved, 2=rejected, 3=discontinued
    ApprovalBy     string
}

This should allow you to have multiple edits from different users in the queue, CRUD a specific edit, and or filter edits by their status.