marcusolsson / goddd Goto Github PK
View Code? Open in Web Editor NEWExploring DDD in Go
License: MIT License
Exploring DDD in Go
License: MIT License
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?
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:
Where should I put the transaction implementation? It should not be in the repository layer no?
mock
package.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 ๐
Currently, no implementation is used.
Line 66 in 9042214
Proposal is to add a new amqp
subpackage that implements the inspection.EventHandler
interface.
What's an idiomatic way to add authentication to goddd?
I think I should perform following operation for each request:
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?
how did you generate this ?
Reminds me of swagger
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
main.go
is currently in root directory because of issues when deploying it to Heroku.
/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
}
Currently, the routing
package contains the routing.Service
interface as well as the proxy service implementation. This is a pragmatic solution but it might be better ways of separating them.
The middleware
package has too much responsibility. Extract a new package server
with subpackages for each endpoint.
First reported here
Now using query parameters
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?
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.
> 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
> 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.
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
.
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.
The current domain validation is a bit lacking. Let's discuss some alternatives in this issue:
The simplest way would be to go all stdlib and just extract the validation into its own function:
func Validate(c Cargo) error
It could be interesting to take a closer look at validator
: https://github.com/go-playground/validator
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...
Only needs BookingService as the other repositories can be accessed through it.
Remove unnecessary complexity. Just do the job.
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!
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:
routing
domain package. This mixes infrastructure with domain logic though.middleware
, impl
, infrastructure
etc.Make
cargo.HandlingActivity
cargo.HandlingEvent
cargo.HandlingHistory
into
handling.Activity
handling.Event
handling.History
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.
The Clean Architecture structure feels out of place. Bring packages to root directory instead of hiding them in domain, infrastructure etc.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.