2017-03-21 10 views
0

私は、Goを使用して投影を作成する簡単なイベントストアを作成する方法を学びたいと考えています。私は、混合型の構造体を含むスライスやマップを操作する方法について立ち往生しています。この点は、IEntityとIEventをさまざまなフィールドで必要に応じて実装する多くの構造体を開発者が作成してほしいということです。混合構造体タイプのマップとスライスを移動

C/C++/Javaの基本的な知識があるJavaScript/Node.jsのバックグラウンドがありますが、ここではクラス/継承パターンについて多くのことを考えています。行く。私が手

package main 

import (
    "sync" 
    "time" 

    uuid "github.com/satori/go.uuid" 
) 

// IEntity describes an entity, a struct that is the sum of all events resolved in a chronological order 
type IEntity interface { 
} 

// IProjection describes the interface for a projection struct 
type IProjection interface { 
    Lock() 
    Unlock() 
    Get(id uuid.UUID) IEntity 
    Set(id uuid.UUID, entity IEntity) 
    GetLastEventTime() time.Time 
    Append(event IEvent) 
} 

// IEvent describes the interface for a event, any event added to the system needs to implement this interface 
type IEvent interface { 
    Resolve(projection IProjection) 
} 

// EventVault is the base struct that keeps all the events and allows for projections to be created 
type EventVault struct { 
    sync.Mutex 
    events []IEvent 
} 

// Append adds a IEvent to the projection and runs that events IEvent.Resolve method 
func (vault *EventVault) Append(event IEvent) { 
    vault.Lock() 
    defer vault.Unlock() 
    vault.events = append(vault.events, event) 
} 

// Project creates a projection with entities from all events up until the choosen end time 
func (vault *EventVault) Project(endTime time.Time, projection IProjection) IProjection { 
    lastEventTime := projection.GetLastEventTime() 
    for index := range vault.events { 
     event := vault.events[index] 
     if event.Time.After(lastEventTime) && event.Time.Before(endTime) { 
      projection.Append(event) 
     } 
    } 
    return projection 
} 

// Projection caculates and stores a projection of the events appended to it with the Append method 
type Projection struct { 
    sync.Mutex 
    events []IEvent 
    entities map[uuid.UUID]IEntity 
} 

// Get returns an IEntity struct from an id (for use in the IEvent.Resolve method) 
func (projection *Projection) Get(id uuid.UUID) IEntity { 
    return projection.entities[id] 
} 

// Set add a IEntity to the projection (for use in the IEvent.Resolve method) 
func (projection *Projection) Set(id uuid.UUID, entity IEntity) { 
    projection.entities[id] = entity 
} 

// GetLastEventTime returns the time for the event that was added last 
func (projection *Projection) GetLastEventTime() time.Time { 
    event := projection.events[len(projection.events)-1] 

    if event == nil { 
     return time.Unix(0, 0) 
    } 

    return event.Time 
} 

// Append adds a IEvent to the projection and runs that events IEvent.Resolve method 
func (projection *Projection) Append(event IEvent) { 
    projection.Lock() 
    projection.events = append(projection.events, event) 
    event.Resolve(projection) 
    projection.Unlock() 
} 

// ------------------ Below is usage of above system ------------------ 

// PlayerEntity is a sample entity that can be used for testing 
type PlayerEntity struct { 
    ID  uuid.UUID 
    Name  string 
    Birthday time.Time 
    UpdatedAt time.Time 
} 

// AddPlayerEvent is a sample event that can be used for testing 
type AddPlayerEvent struct { 
    ID uuid.UUID 
    Time time.Time 
    Name string 
} 

// Resolve will create a new PlayerEntity and add that to the projection 
func (event *AddPlayerEvent) Resolve(projection IProjection) { 
    player := PlayerEntity{ID: uuid.NewV4(), Name: event.Name, UpdatedAt: event.Time} 
    projection.Set(player.ID, &player) 
} 

// SetBirthdayPlayerEvent is a sample event that can be used for testing 
type SetBirthdayPlayerEvent struct { 
    ID  uuid.UUID 
    Time  time.Time 
    Birthday time.Time 
} 

// Resolve will change the name on a PlayerEntity 
func (event *SetBirthdayPlayerEvent) Resolve(projection IProjection) { 
    player := *projection.Get(event.ID) 
    player.Birthday = event.Birthday 
    player.UpdatedAt = event.Time 
} 

func main() { 
    vault := EventVault{} 
    event1 := AddPlayerEvent{ID: uuid.NewV4(), Time: time.Now(), Name: "Lisa"} 
    vault.Append(&event1) 
    birthday, _ := time.Parse("2006-01-02", "2017-03-04") 
    event2 := SetBirthdayPlayerEvent{ID: event1.ID, Time: time.Now(), Birthday: birthday} 
    vault.Append(&event2) 
} 

エラーが

./main.go:47: event.Time undefined (type IEvent has no field or method Time) 
./main.go:79: event.Time undefined (type IEvent has no field or method Time) 
./main.go:122: invalid indirect of projection.Get(event.ID) (type IEntity) 

構造体の種類は、おそらく私は地図やスライスにどのようにそれらを置くためにそれらを格納する別の方法が必要に失われていますか? golangで

+3

を欽慕しようと、私たちはすることになっています122行をカウントするまで指で画面を動かすか、問題がどこにあるかを指摘したいのですか? :) – hobbs

+1

私があなたを正しく理解していれば、おそらく 'Time()time.Time'をあなたの' IEvent'インターフェースに追加することができます。さらに、構造体に 'Time()time.Time'というメソッドを実装し、与えられた行を' event.Time() 'に更新する必要があります。 –

+0

はい、これには2つの問題があります.1つはすべてのプロパティに対してgetter/setterを追加する必要があり、2つはエンティティを追加する場合です。 TreeEntityと呼ばれるプロパティがKind文字列、Branches [] Branchなどであるため、異なるプロパティを持つためにResolveメソッドが破損するため、getter/setterが異なります。 :/ 私はゆっくりタイプの言語に慣れています:/ – Tirithen

答えて

1

あなたは

type IEntity interface { 
} 

のようなインターフェースを宣言するときには、型キャストせずに、このインターフェースで行うことができるすべてのものを定義します。ここでは、このインターフェースの機能を定義していません。あなたはそれを

type IEntity interface { 
    Time() time.Time 
} 

のような方法を与えることを持っているよりも機能が必要な場合はそのインターフェイスで使用することを希望する任意のタイプは、これらの機能は、次のことができその後

func (a AddPlayerEvent) Time() time.Time { 
    return a.Time 
} 

See the docs

、すなわち実装する必要がありますこれらの方法のいずれかを使用してください

func (projection *Projection) Append(event IEvent) { 
    ... 
    event.Time() 
    ... 
} 

2詳細S

  • あなたはKを使用することができますマップを反復、V:=レンジmy_map
  • * projection.Get(event.ID)非ポインタ型
+0

答えは、構造体に追加されたすべてのプロパティに対してgetters/setterを使用しなければならないということです、それは私が望んでいた答えではありません。唯一の方法。インターフェースが必要でない場合でも、getter/setterを必要とせずに、スライス/マップ内の参照を構造体の組み合わせに保存する別の方法があれば、すばらしいでしょう。 私はgetters/settersを成功させることなく試しましたが、私はこれについて私の頭を掴むためにもう少し練習する必要があると思います。入力いただきありがとうございます。 – Tirithen

+1

正しいことですが、構造体のメソッド/ストアに渡す型としてインターフェイスを使用する必要はありませんが、インターフェイスを使用する場合は、必要なすべてのメソッドがインターフェイスより優れていることを明確にしてください。 –

+0

player.Birthdayをセッターに変更するには、SetBirthday(time time.Time)をIEntityに追加する必要があります。これは、すべてのエンティティにプロパティを設定する必要がないため、BirthdayはIEntityという汎用インターフェイスを持つ完全な点を壊します。 :/ – Tirithen

関連する問題