the repository itself is an individual package • Flat Packages Makes the purpose of each package clear, and separates accordingly. • Multiple Packages Separated by each design pattern, such as MVC, DDD etc.
get coverage • Good for things that can be done with a simple structure like libraries etc. . ├── ctx.go ├── ctx_test.go ├── debug.go ├── debug_test.go ├── error.go ├── error_test.go ├── handler.go ├── handler_func.go ├── handler_func_test.go ├── jsonrpc.go ├── jsonrpc_test.go ├── method.go ├── method_test.go ├── parse.go └── parse_test.go
B Feature C Feature A, Feature B, Feature C Feature A, Feature B, Feature C Feature A, Feature B, Feature C Flat Packages Categorized based on different features Multiple Packages Categorized based on design patterns
• Even if you traverse the layers, it settles down at the second level • As roles are clear for each package, they are loose coupling • Good for Micro Services and Middleware etc. . ├── ctx │ ├── ctx.go │ └── ctx_test.go ├── debug │ ├── debug.go │ └── debug_test.go ├── error │ ├── error.go │ └── error_test.go ├── handler │ ├── handler.go │ ├── handler_func.go │ └── handler_func_test.go ├── jsonrpc.go ├── jsonrpc_test.go ├── parse │ ├── parse.go │ └── parse_test.go └── method ├── method.go └── method_test.go
managed with one repository As it uses GAE, it works well with a Project Based GOPATH. • Uses various design patterns that simplify the DDD It is close to the composition of our previous Mercari Atte application and accounts for human resource mobility
Existence of the library directory Package group forms a complete unit Lightweight DDD . └── kauru ├── application ├── domain ├── infrastructure ├── interfaces └── library
composed of flat packages The most important thing is cutting the packages appropriately • Even within the layer, we need to protect the domain layer above all else If the Domain fails, we’ll be in cycle import hell and it will become weak to changes. • If you’re lost, choose the most simple path I like the quote “Keep It Simple Stupid”
object oriented language?” http://postd.cc/is-go-object-oriented/ This post from @spf13 is really helpful. • What does “object oriented” here refer to? a. It doesn’t refer to the program as code and data, but uses the concept of an “object” to integrate the two b. An object is an abstract data type that has a condition (data) and a behavior (code)
{ Waddle() (lat, lng float64) } Quacker interface { Quack() string } ) • Defined method list • An interface without a method is interface{} • All defined types fulfill interface{} How to Define the Interface
(o *Osamingo) Waddle() (float64, float64) { return o.Lat, o.Lng } func (o *Osamingo) Quack() string { return "Hey" } func main() { var o interface{} = &Osamingo{ Lat: 35.662056, Lng: 139.728451, } switch o.(type) { case Duck: fmt.Println("Hi, duck!") } } How to Implement the Interface • Similar to Duck Typing • If you implement Waddler and Quacker, it will become Duck.
the Database • An example of what we are actually doing with Mercari Kauru. • Will introduce with the following steps a. Entity interface design b. User entity implementation c. UserRepository interface design d. UserRepository implementation e. Cloud Datastore library function wrap
*User) GetID() string { return u.ID } func (u *User) SetID(id string) { u.ID = id } func (u *User) Load(p []datastore.Property) error { return Load(u, p) } func (u *User) Save() ([]datastore.Property, error) { return Save(u) } • Implementing EntityBehavior for the user • For Load and Save, prepare a common function within the entity
interface{}, p []datastore.Property) error { err := datastore.LoadStruct(dst, p) if err != nil { return err } return LoadTimestamps(dst, p) } func LoadTimestamps(i interface{}, p []datastore.Property) error { ut, isUT := i.(UpdateTimestamper) if !isUT { return nil } for i := range p { if p[i].Name == "updated_at" { ut.SetUpdatedAt(p[i].Value.(time.Time)) } } return nil } • Uses App Engine library’s LoadStruct • If xTimestamper is implemented, it will be loaded
{ p, err := datastore.SaveStruct(src) if err != nil { return nil, err } return append(p, SaveTimestamps(src)...), nil } func SaveTimestamps(i interface{}) []datastore.Property { p := make([]datastore.Property, 0, 1) if ut, ok := i.(UpdateTimestamper); ok { ut.SetUpdatedAt(time.Now()) return append(nil, datastore.Property{ Name: "updated_at", Value: ut.GetUpdatedAt(), }) } return nil } • Uses the App Engine library’s savestruct • If xTimestamper is implemented, a time will be set Entity’s Save Function
{ Get(c context.Context, id int64) (*domain.User, error) Save(c context.Context, u *domain.User) error } var Repository repository • Java Interface definition • The Infrastructure layer’s persistence package is in charge of implementation
datastoreRepository func NewUserRepository() *UserRepository { return &UserRepository{ kind: "User", } } func (r *UserRepository) Get(c context.Context, id int64) (*domain.User, error) { u := &domain.User{} u.SetID(id) return u, get(c, r.kind, u) } func (r *UserRepository) Save(c context.Context, u *domain.User) error { return put(c, r.kind, u) } • DI the domain layer from the Infrastructure layer • For get and put, prepare a common function within the repository.
entity and as a repository For the Entity, expect an action For the Repository, expect DI • Wrap the App Engine library and use it Use the interface as an entity
logic expands into the application layer and enlarges internal packages more than required. => In order to prioritize development speed, I tried my best to implement DRY => However, it is true that DDD and DRY are not compatible => Remove the reference from the interface, Application layers to the infrastructure layer.
initialization processing becomes split up into interface layer and infrastructure layer parts. => Register JSON-RPC method in the interface layer => DI of domain repository in the infrastructure layer => You’re really not supposed to initialize on each layer… (ヽ´ω`) => Start an application and streamline initialization processing within the main function.
are some of the more commonly used => Extracting the Datastore library as a loosely wrapped construct makes it easier to generalize and maintain => Most new services within Souzoh use GAE and Datastore, which helps improve the synergy => Will continue to create libraries for sharing, adding test functionality as well
you are going to use before designing. Dev will be smoother if you consider what each language is best at beforehand • It is hard to be perfect from the start. However, it is possible to make it adaptable to change. It is not good to create a condition where special cases are not accepted right from the start.