Git Product home page Git Product logo

goddd's Introduction

GoDDD

Build Status GoDoc Go Report Card License MIT stability-unstable

This is an attempt to port the DDD Sample App to idiomatic Go. This project aims to:

  • Demonstrate how the tactical design patterns from Domain Driven Design may be implemented in Go.
  • Serve as an example of a modern production-ready enterprise application.

Important note

This project is intended for inspirational purposes and should not be considered a tutorial, guide or best-practice neither how to implement Domain Driven Design nor enterprise applications in Go. Make sure you adapt the code and ideas to the requirements of your own application.

Porting from Java

The original application is written in Java and much thought has been given to the domain model, code organization and is intended to be an example of what you might find in an enterprise system.

I started out by first rewriting the original application, as is, in Go. The result was hardly idiomatic Go and I have since tried to refactor towards something that is true to the Go way. This means that you will still find oddities due to the application's Java heritage. If you do, please let me know so that we can weed out the remaining Java.

Running the application

Start the application on port 8080 (or whatever the PORT variable is set to).

go run main.go -inmem

If you only want to try it out, this is enough. If you are looking for full functionality, you will need to have a routing service running and start the application with ROUTINGSERVICE_URL (default: http://localhost:7878).

Docker

You can also run the application using Docker.

# Start routing service
docker run --name some-pathfinder marcusolsson/pathfinder

# Start application
docker run --name some-goddd \
  --link some-pathfinder:pathfinder \
  -p 8080:8080 \
  -e ROUTINGSERVICE_URL=http://pathfinder:8080 \
  marcusolsson/goddd -inmem

... or if you're using Docker Compose:

docker-compose up

Try it!

# Check out the sample cargos
curl localhost:8080/booking/v1/cargos

# Book new cargo
curl localhost:8080/booking/v1/cargos -d '{"origin": "SESTO", "destination": "FIHEL", "arrival_deadline": "2016-03-21T19:50:24Z"}'

# Request possible routes for sample cargo ABC123
curl localhost:8080/booking/v1/cargos/ABC123/request_routes

Contributing

If you want to fork the repository, follow these step to avoid having to rewrite the import paths.

go get github.com/marcusolsson/goddd
cd $GOPATH/src/github.com/marcusolsson/goddd
git remote add fork git://github.com:<yourname>/goddd.git

# commit your changes

git push fork

For more information, read this.

Additional resources

For watching

For reading

Related projects

The original application uses a external routing service to demonstrate the use of bounded contexts. For those who are interested, I have ported this service as well:

pathfinder

To accompany this application, there is also an AngularJS-application to demonstrate the intended use-cases.

dddelivery-angularjs

Also, if you want to learn more about Domain Driven Design, I encourage you to take a look at the Domain Driven Design book by Eric Evans.

goddd's People

Contributors

jurre avatar lordalek avatar marcusolsson avatar tarekbadrshalaan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

goddd's Issues

Does the implementation of routing service really belong to booking?

The routing service is a domain service that may be used by multiple application services. Even so, currently it resides in the booking package.

Discussion:

Where should the implementation of domain services be placed?

Alternatives:

  • Keep implementation in the routing domain package. This mixes infrastructure with domain logic though.
  • Place the implementation in a separate package, middleware, impl, infrastructure etc.
  • Other alternative?

Consolidate time formats

Currently, when booking a cargo using the REST API the arrival deadline requires to be of UNIX timestamp format. It should be changed to use ISO 8601.

Returning interfaces?

Hi, Very new to GO/Programming here, very interested in implementing DDD. Having a look over your code and just have a question.

Why did you choose to return/export interfaces rather than structs/concrete types? Was it to keep from having to retype the same interface in each service?

Haven't quite figured everything out yet, maybe there is something else I'm missing?

For example the CargoRepository interface is declared in in cargo.go rather then where it is consumed?

type CargoRepository interface {
	Store(cargo *Cargo) error
	Find(id TrackingID) (*Cargo, error)
	FindAll() []*Cargo
}

Why not do something like this in your service.go?

type cargoRepository interface {
	Store(cargo *shipping.Cargo) error
	Find(id shipping.TrackingID) (*shipping.Cargo, error)
	FindAll() []*shipping.Cargo
}

type Service struct {
	cargos         cargoRepository
}

func (s *Service) Track() {
//...
}

func NewService(cargos cargoRepository) *Service {
	return &Service{
		cargos:         cargos,
	}
}

Reference:
https://github.com/golang/go/wiki/CodeReviewComments#interfaces
https://mycodesmells.com/post/accept-interfaces-return-struct-in-go
https://stackoverflow.com/questions/37181597/go-should-i-use-an-interface-to-allow-for-mocking

RequestPossibleRoutesForCargo returns domain objects

The booking service method RequestPossibleRoutesForCargo returns []cargo.Itinerary which causes a DTO to be created in the makeRequestRoutesEndpoint function. It should be assembled in the application service the way it's currently done for Cargo.

Flatten directory structure

The Clean Architecture structure feels out of place. Bring packages to root directory instead of hiding them in domain, infrastructure etc.

Examples using SQL and relational models.

First off, thanks so much for creating this repository. As a relative newcomer to Go, orienting myself in a landscape without established or strictly enforced best practices has been the toughest part of starting and this repository has been a godsend in terms of strategizing about project structure.

I've been having some trouble applying some of the principles here to my real-world services, however. Primarily, I have been having difficulty figuring out how to structure my models in a relational way, or rather where it is appropriate to load related models (repository, service, etc)? I'm using jmoiron/sqlx and also dealing with how to expand the repository interface to cover some more relational semantics that are not covered by the No-SQL example given in the repository.

For instance, only Store(obj *Object) is covered by the repository, but what if I'd like to handle the case of persisting a new object independently of persisting an existing object? In my case, I need to assign a custom ID to the new object and perhaps hash a user's password or something to that effect. Where would this best be handled and how?

Docker example failure to start.

I'm having an issue testing Goddd demo app, after watching presentation https://www.youtube.com/watch?v=twcDf_Y2gXY.

Any idea what can possibly be the issue? Thanks in advance for your time.

# Start routing service - in terminal 1 pass

> docker run --name some-pathfinder marcusolsson/pathfinder 
Unable to find image 'marcusolsson/pathfinder:latest' locally
latest: Pulling from marcusolsson/pathfinder
911c6d0c7995: Pull complete 
1edb6ba6b964: Pull complete 
fad95b9d855a: Pull complete 
0dd6a60995af: Pull complete 
Digest: sha256:1829ebcaf75240aec855f43e87b6d4f6214493a509c7ab0a2cc03da40788761f
Status: Downloaded newer image for marcusolsson/pathfinder:latest
ts=2018-09-16T14:13:24.3559957Z transport=http address=:8080 msg=listening

# Start application - in terminal 2 fail

>  docker run --name some-goddd \
>  --link some-pathfinder:pathfinder \
>  -p 8080:8080 \
>   -e ROUTINGSERVICE_URL=http://pathfinder:8080 \
>   marcusolsson/goddd /goddd -inmem
Unable to find image 'marcusolsson/goddd:latest' locally
latest: Pulling from marcusolsson/goddd
911c6d0c7995: Already exists 
bf860b7c31a6: Pull complete 
7e1a6e706574: Pull complete 
9b451e13c5e3: Pull complete 
51256e166ac3: Pull complete 
2035a10c322a: Pull complete 
Digest: sha256:7665533494bf93ea512ec7e6fda4618c319c71013d0a663235df745c940354be
Status: Downloaded newer image for marcusolsson/goddd:latest
panic: no reachable servers

goroutine 1 [running]:
main.main()
	/go/src/github.com/marcusolsson/goddd/cmd/shippingsvc/main.go:74 +0x1dc2

Changing port 8080 -e ROUTINGSERVICE_URL=http://pathfinder:8080 to -e ROUTINGSERVICE_URL=http://pathfinder:7878 still have the same result.

Idiomatic way of attaching authentication

What's an idiomatic way to add authentication to goddd?

I think I should perform following operation for each request:

  • extract and validate jwt token,
  • check if user exists in repository,
  • attach user object to context and
  • load user info from context when needed.

After that each endpoint can have different middleware for authorization such RequireAdmin, RequireLogin, RequireRole etc.
Should this functionality be added as another application service? Or as infrastructure service like logging? Maybe another way? How can I add a middleware that handle every request?

Adopt a package management tool / makefile

Hello! Participated at Goconf2016, awesome talk :-)
So I've forked this repo but run into trouble when trying to run it. Essentially the compiler first looks into the /vendor folder, but since in the code the imports are specified as github.com/marcusolsson/ it doesn't work when I clone the project in the local folder github.com/edoardo849.

My suggestion could be to use a package management tool like glide to fix the problems OR to specify in the README to create the folder GOPATH/src/github.com/marcusolsson and to git clone there.

Sample output:

./main.go:86: cannot use session (type *"github.com/edoardo849/goddd/vendor/gopkg.in/mgo.v2".Session) as type *"github.com/marcusolsson/goddd/vendor/gopkg.in/mgo.v2".Session in argument to mongo.NewCargoRepository
./main.go:87: cannot use session (type *"github.com/edoardo849/goddd/vendor/gopkg.in/mgo.v2".Session) as type *"github.com/marcusolsson/goddd/vendor/gopkg.in/mgo.v2".Session in argument to mongo.NewLocationRepository
./main.go:88: cannot use session (type *"github.com/edoardo849/goddd/vendor/gopkg.in/mgo.v2".Session) as type *"github.com/marcusolsson/goddd/vendor/gopkg.in/mgo.v2".Session in argument to mongo.NewVoyageRepository
./main.go:89: cannot use session (type *"github.com/edoardo849/goddd/vendor/gopkg.in/mgo.v2".Session) as type *"github.com/marcusolsson/goddd/vendor/gopkg.in/mgo.v2".Session in argument to mongo.NewHandlingEventRepository

I was also thinking that maybe it will be easier to just have a makefile to make run and make docker-run to do everything. If you agree I can create it myself and create a pull request. Let me know!

Improve test suites

  • gocheck helped when recreating the test cases from the original Java application but now's the time revisit, and rewrite them as stdlib tests instead.
  • Many of the tests are using in-memory repositories. The tests should (where sense is made) instead be using the mock repositories from the mock package.

Database transactions

Where should I put database transactions. Let's say I am building an e-commerce website, and I want to create a feature to order an item with a third-party payment. The simplified flow:

  1. Deduct item stock in database
  2. Charge to Paypal
  3. Create invoice in database

Where should I put the transaction implementation? It should not be in the repository layer no?

Moving source code to src/pkg/app directory

Its just my opinion, but I think it will be much cleaner if you would take all the code that
is connected to the project itself and put it into an additional directory.
I have red that you want it to be as flat as possible, and it will be still flat, the code that matters, the way you implement DDD, all that is related somehow to the domain, application and infrastructure layers will all be in the same level as it is right now, but only inside another directory, separated from the other files that have a different purpose.
And then at the first level of the folder tree you would have the cmd directory, pkg (or src or app or whatever you want to call it) and all the other remaining files that are related to setting up the application itself, like docker files etc...

improving package names

Thanks for creating this repository. Now, I am studying with this.
Especially, I felt interested in wrapping service.

var bs booking.Service
bs = booking.NewService(..)
bs = booking.NewLoggingService(...)

As far as I know, DDD has layerd architecture, but packages on this code is placed in flat.
When I develop golang web application, for example, I often write as below:

interfaces/booking/
interfaces/routing/
application/booking/
domain/cargo/
infrastructure/inmem/
infrastructure/mongo/
...

I think this is better, but what do you think?
I am happy if you check it out ๐Ÿ˜„

Application services use both DTOs and domain objects

In the original Java version, the application services are divided into a service using domain objects and a facade that accepts DTOs, converts them to domain objects and dispatches them.

Should request/response structs contain DTOs or domain objects?.
If request contains DTOs, where should translation occur?

I'm reluctant to place translation in application service since it should focus on fulfilling the use case.

Alternatives?

Extract server package

The middleware package has too much responsibility. Extract a new package server with subpackages for each endpoint.

First reported here

invalid memory address or nil pointer dereference

/inspection/inspection.go

	if c.Delivery.IsMisdirected {
		s.handler.CargoWasMisdirected(c)      //<-- s.handle is nil
	}

	if c.Delivery.IsUnloadedAtDestination {
		s.handler.CargoHasArrived(c).               //<-- s.handle is nil
	}

Question about aggregates

Hello Marcus,

New ddd guy here, and I wanted to ask you if the cargo struct is considered to be an aggregate.

If possible labeling of tactical type in the comments would help as a reference example.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.