Skip to content

Commit

Permalink
Add examples
Browse files Browse the repository at this point in the history
  • Loading branch information
Kat committed Jun 2, 2018
0 parents commit 35dbc16
Show file tree
Hide file tree
Showing 93 changed files with 3,991 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
.idea
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2018 Kat

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
3 changes: 3 additions & 0 deletions domain-driven-hex/Gopkg.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[constraint]]
name = "github.com/julienschmidt/httprouter"
version = "1.1.0"
26 changes: 26 additions & 0 deletions domain-driven-hex/cmd/data/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import (
"fmt"

"github.com/katzien/structure-examples/domain-driven/storage"
"github.com/katzien/structure-examples/domain-driven/adding"
"github.com/katzien/structure-examples/domain-driven/reviewing"
)

func main() {

// error handling omitted for simplicity
beersStorage, _ := storage.NewJSONBeerStorage()
reviewsStorage, _ := storage.NewJSONReviewStorage()

// create the available services
adder := adding.NewService(beersStorage)
reviewer := reviewing.NewService(reviewsStorage)

// add some sample data
adder.AddSampleBeers()
reviewer.AddSampleReviews()

fmt.Println("Finished adding sample data.")
}
56 changes: 56 additions & 0 deletions domain-driven-hex/cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"fmt"
"log"
"net/http"

"github.com/julienschmidt/httprouter"
"github.com/katzien/structure-examples/domain-driven/beers"
"github.com/katzien/structure-examples/domain-driven/reviews"
"github.com/katzien/structure-examples/domain-driven/storage"
"github.com/katzien/structure-examples/domain-driven/adding"
"github.com/katzien/structure-examples/domain-driven/reviewing"
"github.com/katzien/structure-examples/domain-driven/listing"
)

func main() {

// set up storage
storageType := storage.InMemory // this could be a flag; hardcoded here for simplicity

var beersStorage beers.Repository
var reviewsStorage reviews.Repository

switch storageType {
case storage.InMemory:
beersStorage = new(storage.MemoryBeerStorage)
reviewsStorage = new(storage.MemoryReviewStorage)
case storage.JSONFiles:
// error handling omitted for simplicity
beersStorage, _ = storage.NewJSONBeerStorage();
reviewsStorage, _ = storage.NewJSONReviewStorage();
}

// create the available services
adder := adding.NewService(beersStorage)
reviewer := reviewing.NewService(reviewsStorage)
lister := listing.NewService(beersStorage, reviewsStorage)

// add some sample data
adder.AddSampleBeers()
reviewer.AddSampleReviews()

// set up the HTTP server
router := httprouter.New()

router.GET("/beers", listing.MakeGetBeersEndpoint(lister))
router.GET("/beers/:id", listing.MakeGetBeerEndpoint(lister))
router.GET("/beers/:id/reviews", listing.MakeGetBeerReviewsEndpoint(lister))

router.POST("/beers", adding.MakeAddBeerEndpoint(adder))
router.POST("/beers/:id/reviews", reviewing.MakeAddBeerReviewEndpoint(reviewer))

fmt.Println("The beer server is on tap now: http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", router))
}
27 changes: 27 additions & 0 deletions domain-driven-hex/pkg/adding/endpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package adding

import (
"github.com/julienschmidt/httprouter"
"net/http"
"encoding/json"
"github.com/katzien/structure-examples/domain-driven/beers"
)

// MakeAddBeerEndpoint creates a handler for POST /beers requests
func MakeAddBeerEndpoint(s Service) func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
decoder := json.NewDecoder(r.Body)

var newBeer beers.Beer
err := decoder.Decode(&newBeer)
if err != nil {
http.Error(w, "Bad beer - this will be a HTTP status code soon!", http.StatusBadRequest)
return
}

s.AddBeer(newBeer)

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode("New beer added.")
}
}
34 changes: 34 additions & 0 deletions domain-driven-hex/pkg/adding/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package adding

import (
"github.com/katzien/structure-examples/domain-driven/beers"
)

// Service provides beer or review adding operations
type Service interface {
AddBeer(b ...beers.Beer)
AddSampleBeers()
}

type service struct {
bR beers.Repository
}

// NewService creates an adding service with the necessary dependencies
func NewService(bR beers.Repository) Service {
return &service{bR}
}

// AddBeer adds the given beer(s) to the database
func (s *service) AddBeer(b ...beers.Beer) {
for _, beer := range b {
_ = s.bR.Add(beer) // error handling omitted for simplicity
}
}

// AddSampleBeers adds some sample beers to the database
func (s *service) AddSampleBeers() {
for _, b := range beers.DefaultBeers {
_ = s.bR.Add(b) // error handling omitted for simplicity
}
}
27 changes: 27 additions & 0 deletions domain-driven-hex/pkg/beers/beer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package beers

import (
"time"
"errors"
)

// Beer defines the properties of a reviewable beer
type Beer struct {
ID int `json:"id"`
Name string `json:"name"`
Brewery string `json:"brewery"`
Abv float32 `json:"abv"`
ShortDesc string `json:"short_description"`
Created time.Time `json:"created"`
}

// ErrUnknown is used when a beer could not be found.
var ErrUnknown = errors.New("unknown beer")
var ErrDuplicate = errors.New("beer already exists")

// Repository provides access to the list of beers.
type Repository interface {
GetAll() []Beer
Get(id int) (Beer, error)
Add(Beer) error
}
115 changes: 115 additions & 0 deletions domain-driven-hex/pkg/beers/sample_beers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package beers

import "time"

var DefaultBeers = []Beer{
{
ID: 1,
Name: "Pliny the Elder",
Brewery: "Russian River Brewing Company",
Abv: 8,
ShortDesc: "Pliny the Elder is brewed with Amarillo, " +
"Centennial, CTZ, and Simcoe hops. It is well-balanced with " +
"malt, hops, and alcohol, slightly bitter with a fresh hop " +
"aroma of floral, citrus, and pine.",
Created: time.Date(2017, time.October, 24, 22, 6, 0, 0, time.UTC),
},
{
ID: 2,
Name: "Oatmeal Stout",
Brewery: "Samuel Smith",
Abv: 5,
ShortDesc: "Brewed with well water (the original well at the " +
"Old Brewery, sunk in 1758, is still in use, with the hard well " +
"water being drawn from 85 feet underground); fermented in " +
"‘stone Yorkshire squares’ to create an almost opaque, " +
"wonderfully silky and smooth textured ale with a complex " +
"medium dry palate and bittersweet finish.",
Created: time.Date(2017, time.October, 24, 22, 12, 0, 0, time.UTC),
},
{
ID: 3,
Name: "Märzen",
Brewery: "Schlenkerla",
Abv: 5,
ShortDesc: "Bamberg's speciality, a dark, bottom fermented " +
"smokebeer, brewed with Original Schlenkerla Smokemalt from " +
"the Schlenkerla maltings and tapped according to old tradition " +
"directly from the gravity-fed oakwood cask in the historical " +
"brewery tavern.",
Created: time.Date(2017, time.October, 24, 22, 17, 0, 0, time.UTC),
},
{
ID: 4,
Name: "Duvel",
Brewery: "Duvel Moortgat",
Abv: 9,
ShortDesc: "A Duvel is still seen as the reference among strong " +
"golden ales. Its bouquet is lively and tickles the nose with an " +
"element of citrus which even tends towards grapefruit thanks to " +
"the use of only the highest-quality hop varieties.",
Created: time.Date(2017, time.October, 24, 22, 24, 0, 0, time.UTC),
},
{
ID: 5,
Name: "Negra",
Brewery: "Modelo",
Abv: 5,
ShortDesc: "Brewed longer to enhance the flavors, this Munich " +
"Dunkel-style Lager gives way to a rich flavor and remarkably " +
"smooth taste.",
Created: time.Date(2017, time.October, 24, 22, 27, 0, 0, time.UTC),
},
{
ID: 6,
Name: "Guinness Draught",
Brewery: "Guinness Ltd.",
Abv: 4,
ShortDesc: "Pours dark brown, almost black with solid lasting light brown head. " +
"Aroma of bitter cocoa, light coffee and roasted malt. " +
"Body is light sweet, medium bitter. " +
"Body is light to medium, texture almost thin and carbonation average. " +
"Finish is medium bitter cocoa with more pronounced roast flavor. Smooth drinker.",
Created: time.Date(2017, time.October, 24, 22, 27, 0, 0, time.UTC),
},
{
ID: 7,
Name: "XX Lager",
Brewery: "Cuahutemoc Moctezuma",
Abv: 4.2,
ShortDesc: "A crisp, refreshing, light-bodied malt-flavored beer with a well-balanced finish. " +
"A Lager that drinks like a Pilsner. A liquid embodiment of living life to the fullest. " +
"A beverage made from pure spring water and the choicest hops. A beer with such good taste, it’s chosen you to drink it.",
Created: time.Date(2017, time.October, 28, 15, 02, 0, 0, time.UTC),
},
{
ID: 8,
Name: "Tecate",
Brewery: "Cuahutemoc Moctezuma",
Abv: 5,
ShortDesc: "Very smooth, medium bodied brew. Malt sweetness is thin, and can be likened to diluted sugar water. " +
"Touch of fructose-like sweetness. Light citric hop flavours gently prick the palate with tea-like notes that follow and fade quickly. " +
"Finishes a bit dry with husk tannins and a pasty mouthfeel.",
Created: time.Date(2017, time.October, 28, 15, 07, 0, 0, time.UTC),
},
{
ID: 9,
Name: "Sol",
Brewery: "Cuahutemoc Moctezuma",
Abv: 5,
ShortDesc: "While Corona wins the marketing wars in the U.S., Sol is the winning brand in much of Mexico, despite not being a standout in any respect. " +
"You see the logo plastered everywhere and it’s seemingly on every restaurant and bar menu. Like Corona, it’s simple and inoffensive, " +
"but still slightly more flavorful than your typical American macrobrew. At its best ice cold, and progressively worse as it gets warmer.",
Created: time.Date(2017, time.October, 28, 15, 12, 0, 0, time.UTC),
},
{
ID: 10,
Name: "Corona",
Brewery: "Cuahutemoc Moctezuma",
Abv: 5,
ShortDesc: "One of the five best-selling beers in the world, but it usually tastes better in Mexico, " +
"where the bottles don’t have so much time in transit and on shelves. (Sunlight coming through clear bottles is never a good thing for beer.) " +
"This is the typical “drink all afternoon” beer, working well on its own or with a plate of tacos. Refreshing with a lime.",
Created: time.Date(2017, time.October, 28, 15, 14, 0, 0, time.UTC),
},
}
61 changes: 61 additions & 0 deletions domain-driven-hex/pkg/listing/endpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package listing

import (
"github.com/julienschmidt/httprouter"
"net/http"
"encoding/json"
"strconv"
"fmt"
"github.com/katzien/structure-examples/domain-driven/beers"
)

type Handler func(http.ResponseWriter, *http.Request, httprouter.Params)

// MakeAddBeerEndpoint creates a handler for GET /beers requests
func MakeGetBeersEndpoint(s Service) func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.Header().Set("Content-Type", "application/json")
list := s.GetBeers()
json.NewEncoder(w).Encode(list)
}
}

// MakeAddBeeEndpoint creates a handler for GET /beers/:id requests
func MakeGetBeerEndpoint(s Service) func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
ID, err := strconv.Atoi(p.ByName("id"))
if err != nil {
http.Error(w, fmt.Sprintf("%s is not a valid beer ID, it must be a number.", p.ByName("id")), http.StatusBadRequest)
return
}

beer, err := s.GetBeer(ID)
if err == beers.ErrUnknown {
http.Error(w, "The beer you requested does not exist.", http.StatusNotFound)
return
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(beer)
}
}

// MakeGetBeerReviewsEndpoint creates a handler for GET /beers/:id/reviews requests
func MakeGetBeerReviewsEndpoint(s Service) func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
ID, err := strconv.Atoi(p.ByName("id"))
if err != nil {
http.Error(w, fmt.Sprintf("%s is not a valid beer ID, it must be a number.", p.ByName("id")), http.StatusBadRequest)
return
}

reviews, err := s.GetBeerReviews(ID)
if err != nil {
http.Error(w, fmt.Sprintf("%s is not a valid beer ID.", p.ByName("id")), http.StatusBadRequest)
return
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(reviews)
}
}
Loading

0 comments on commit 35dbc16

Please sign in to comment.