How To Implement Domain-Driven Design (DDD) in Go

แชร์
ฝัง
  • เผยแพร่เมื่อ 12 พ.ย. 2024

ความคิดเห็น • 78

  • @holgerm420
    @holgerm420 ปีที่แล้ว +22

    I am just 1h into the video and I'm in awe at how good of a lecturer you are. One does not often find this quality in a person. Keep up the good work!

    • @holgerm420
      @holgerm420 ปีที่แล้ว +2

      So, first of all, thank you for this lecture. I really enjoyed it and learned a lot, as I struggled to get the bigger picture of DDD - until now!

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว

      Thank you very much for saying those kind words, it warms my heart to hear!
      I am super happy you enjoyed it

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว +1

      This as actually a good idea, I will start working on a Hexagonal Tavern tutorial!
      Give me some time though, switching jobs next month and with Xmas and New years it's gonna be a few weeks!
      I would say that your assumption is correct if this was implemented in a Hexagonal design.
      Again, there are many flavors of doing this out there, but I'd say the services etc would be used in the adapters etc.
      I couldn't say right of the bat how I'd structure it, but I'll make sure to create a Hexagonal Tavern that is controlled as a CLI!
      Thank you for providing an interesting idea

  • @AlokTripathi
    @AlokTripathi ปีที่แล้ว +11

    Better than a lot of courses and tutorials! This is super helpful who wants to learn design pattern with practical usecase! Thanks for the effort Percy!

  • @annusingh4694
    @annusingh4694 ปีที่แล้ว +6

    One thing which I like very much in your teaching style is that you 'speak' your thought process, even write comments sometimes to show what you are going to implement next. This enables this video to be viewed as a guiding excercise, one can pause it at the right time, execute the solution themselves and play again to see how you did it. Thank you.

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว +3

      Thank you for reaching out! I am really glad you enjoy my teaching!
      I am happy to hear you say that you enjoy the thought process, I do try to cover those things because I often find the idea or logic behind the code to be very useful when learning.
      Atleast for myself, I learn faster and remember it longer when I understand the WHY and not only the HOW.
      Your comment made my day!
      Best of luck to you out there, and smash them keyboard keys!

  • @GerbenWijnja
    @GerbenWijnja 2 หลายเดือนก่อน +2

    Great demonstration. I'm 1 hour in, and this code organization makes a lot of sense. I would recommend changing the repository names though. Inside domain/customer/repository.go, the interface can simply be called Repository. And then in domain/customer/memory/repository.go (not memory.go), you can name the concrete implementation also simply Repository. So you have interface customer.Repository, and implementation memory.Repository. Not customer.CustomerRepository and memory.MemoryRepository. That is called "stuttering", and it should always be avoided. Oh and there's a tiny race condition at 44:15. Theoretically someone could add a customer with that id between line 39 and 41. It's safer to move the Lock() before the if on line 38, and don't forget to add an Unlock() before the fmt.Errorf().

  • @stusrambles
    @stusrambles ปีที่แล้ว +2

    This is the best video I've seen on DDD with go. Thankyou!

  • @kevinyu9934
    @kevinyu9934 ปีที่แล้ว +1

    Thanks for sharing this. It literally elevates my unstanding and usage of Golang to another level. 😄

  • @Souljacker7
    @Souljacker7 ปีที่แล้ว +2

    Awesome, we need a FULL COURSE!

  • @maydersonmello
    @maydersonmello ปีที่แล้ว +5

    What amazing content, it was great to see all this working together in a more real example, great patterns too, it would be amazing if you could later bring more content about this, maybe implementing it in a Rest API, thank you very much!

  • @cole.maxwell
    @cole.maxwell ปีที่แล้ว +1

    This was SO good, thank you! You have help put a bunch of design patterns together for me.

  • @vmpartner
    @vmpartner ปีที่แล้ว +1

    Amazing. This is exactly what i'm looking for. Greate video, super clear, thank you!

  • @_mansoor
    @_mansoor ปีที่แล้ว +1

    Awesome Percy, wow you are a wonderful lecturer!!! Great Content by the way... Enjoyed & Learned a Lot. Thank you

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว

      Thank you for the kind words, encourages me

  • @viktorkrepak
    @viktorkrepak 8 หลายเดือนก่อน

    thanks a lot for such a wonderful video! a lot of useful information, right now we're going to improve our backend architecture (along with migration from Python to Go) and I'm looking for different approaches and experience and feel like you video might help us a lot to build a new architecture.

  • @Tay74514
    @Tay74514 ปีที่แล้ว +3

    Thank you for making these tutorials!!

  • @ericzorn3735
    @ericzorn3735 ปีที่แล้ว +2

    This was an amazing DDD tutorial. Just one funny thing was the spelling of peanuts as “peenuts” in your unit tests 😂.

  • @stasjurgensons7008
    @stasjurgensons7008 6 หลายเดือนก่อน

    @programmingpercy
    Aren't your customer and product domains creating separate connections to the same db ? (can be seen around 1:50:00)

  • @tartabullroberto
    @tartabullroberto 10 หลายเดือนก่อน +1

    Nice course, very helpful, thanks

  • @vantoantran2637
    @vantoantran2637 ปีที่แล้ว +1

    You're amazing Percy, keep going!

  • @anshdharia585
    @anshdharia585 ปีที่แล้ว +1

    Hello from Singapore!! Thanks for the video, it is very insightful. You mentioned in the video that this directory structure is not recommended for production use cases. Would be good if you could talk more about production.

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว

      There is a whole video about that in the second part!

  • @acronproject
    @acronproject ปีที่แล้ว

    Thanks for this Mr.Percy

  • @dranon0o
    @dranon0o ปีที่แล้ว +1

    I watched the entire video and the next one but I'm curious on what's the semantic related on how do you integrate:
    - an websocket/http server for having api access to a Service: is there a term in that DDD? is the way we manage repository can fit the case?
    - workers that process data from external sources to be mutated/transformed into our aggregate struct: the DDD show how to manage your own data but what about externals? what about long term processes?
    - utilities that allow you to perform meta programming like reflection on a struct to output data: does DDD is ideal for libraries that does that type of work?
    I have so much questions
    - can we do a reference between two aggregates?: for example we could have entities (temperature, coordinates, etc..) linked to an aggregate (planet) which belong to another aggregate (system) which also belong to another aggregate (galaxy).
    Great video btw, very instructive!

    • @programmingpercy
      @programmingpercy  9 หลายเดือนก่อน

      Thank you for watching! And good questions.
      1. Well, I would say that the API is the Service.
      2. I am not sure I follow the question really
      3. I think DDD Fits this approach also, the meta programming is part of the logic inside a Domain, its just a way of processing logic, wouldnt matter if its regular programming or meta programming.
      4. Yes, you can!

  • @pratyushbarikdev
    @pratyushbarikdev ปีที่แล้ว +1

    Wonderful. Thanks for the video.
    The issue I am facing is about cross-cutting concerns like logging and tracing. Not sure whether I am doing the right structuring or not with them. Bought the blue book. Let's see if it can help with those.

  • @mi_losz
    @mi_losz ปีที่แล้ว +1

    Great materials Percy! 🙂

  • @wobble_cat
    @wobble_cat ปีที่แล้ว +7

    Transaction Value Object be like: Am I a joke for you?

  • @adhiana848
    @adhiana848 ปีที่แล้ว +1

    Thank you for this Great Content!!

  • @danielguldbergaaes6432
    @danielguldbergaaes6432 6 หลายเดือนก่อน

    Such a great video, one thing I noticed is that you some times do "package _test" and "package " for the test files, is there a reason to add or omit the suffix _test ?

    • @programmingpercy
      @programmingpercy  4 หลายเดือนก่อน +1

      Hey, sorry for a late answer. Yes in fact there is.
      Any test that you use the same Package name for will have access to everything internally, (lower case stuff)
      If you add the _test suffix it will test like an External Package.
      Hope that makes sense!

    • @danielguldbergaaes6432
      @danielguldbergaaes6432 4 หลายเดือนก่อน +1

      @@programmingpercy It does, and also solves many of my issues :D Thanks

  • @ilkerkaanadiguzel
    @ilkerkaanadiguzel ปีที่แล้ว +1

    i'm currently at the creating CustomerRepository and I see you don't use pointers when returning aggregate.Customer or accepting it as arguments, the struct could have a lot of data, like a lot of transactions and products, why didn't you use pointer here? How do you decide when or where to use pointers in go?

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว +2

      The base rule is to use pointer whenever you expect the function or the code that accepts the return value to modify the values.
      Since in this case we don't modify the data, no need for a pointer.
      When I code in Go I often overuse pointers since I want to have an easy time to Nil check though.
      But I've noticed that in large code bases pointers are also sometimes confusing, values changing a little bit of everywhere etc.
      So in this case I decided to go for the Non Pointer.
      True, if the structs are very big passing a pointer can have the added feature of performance gains due to avoiding copies.
      Though with all in mind, I do like the standard rule, is values expected to be modified, if not, regular copy, otherwise pointer.
      Makes the code very easy to understand.

  • @rodrigobaliza
    @rodrigobaliza ปีที่แล้ว +1

    great material, thanks a lot for your time!

  • @badhabit6140
    @badhabit6140 9 หลายเดือนก่อน +1

    I'm a bit late to the party, but just finished the vid and had a question about the config pattern you used to hook up the repositories to the services. with that implementation, wouldn't you need to specify the repositories on EVERY implementation of a service? wouldn't it be better to call those configs in the init of a service to avoid having to change the config in every place that you use that service?

    • @programmingpercy
      @programmingpercy  9 หลายเดือนก่อน +1

      That would be possible.
      I'd rather use a constructor func to initialize it and manage it that way though, I am opposed to Init functions for most usecases.
      But you can narrow it down with a constructor func that uses some Default repositories and configs.
      Init funcs have their place but I find it seldom a good approach. Init functions are also great at causing unexpected bugs and frustation in my experience

    • @badhabit6140
      @badhabit6140 9 หลายเดือนก่อน

      @@programmingpercy I like the default repositories approach.
      My concern was mostly around larger codebases where a service is used in a lot of places. If you need to change the repository, you’ll need to update all those areas where the service is being used, which defeats the point of having the config approach to begin with as it’ll be a nightmare to update all those instances.

  • @tanteng1031
    @tanteng1031 ปีที่แล้ว

    Should the aggregate directory be in the domain directory?

  • @RohitKumar-fg1qv
    @RohitKumar-fg1qv 6 หลายเดือนก่อน

    shouldn't the products field in the aggregate.Customer struct just be []uuid instead of []*entity.Item?

  • @Augustine_354
    @Augustine_354 9 หลายเดือนก่อน

    What about generated code (like oapi-codegen)? Then my whole openapi spec is generated into one file. How one should deal with it?

  • @fprotimaru1944
    @fprotimaru1944 ปีที่แล้ว +1

    you saved my life

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว +1

      Thank you, I think saving your life is a bit exaggerated, but I am happy to hear this helped you!

  • @303pix
    @303pix ปีที่แล้ว +1

    i'm watching it and i wanna say thanks in advance: clear and simple.
    But i'm asking myself in Repository Pattern chapter (about 36:00 - 40:00) why checking if person or customers map exist when you get this aggregate by factory method that assure properly init of this entities?

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว +1

      Good question!
      Extra safety and habbit I guess. The factory does prevent it, but somebody can skip using the factory etc.
      Not a strong reason, but I think that was why I added it.

  • @alonsob4069
    @alonsob4069 ปีที่แล้ว +1

    Hi, what if you need to update multiple orders of an user in a single transaction? the steps would be: begin tx, getUser, updateOrders, end tx but due to repositories we can't not start a lets say, postgres/mysql transaction. How can we do this in case we need a transaction through multiple repositores?

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว

      Good question Alonso!
      One thing that I don't show here which I commonly do is to pass in a Database conn in the Repository Factory Func.
      NewMysqlUserDB(db *db.Conn)
      Etc, that allows me to reuse the same connection if I have multiple Databases which are the same and I need to limit the amount of connections.
      That Db Conn can then be stored in the Repository struct and used for creating a TX

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว

      Just realized that doesn't help you.
      You could pass in a Ctx and use the same Root CTX between the repositories and maybe Pass the TX through there, but I've never been a fan of that myself.
      Either the domain scope should be altered if they have such an Relation.
      Another option could be to have the accept an TX of course, Pass in the TX in both Funcs simply and close it afterwards. That would be the most simple solution.
      It would break coupling since it would assume both repositories uses the same db engine I suppose.
      If there is such a coupling and needs to be done that way, I'd say creating the TX I'n a service which holds both repositories and pass the TX in the Funcs from the service, and then end tx in service is the most clean solution.

  • @jerrysumendap2862
    @jerrysumendap2862 ปีที่แล้ว

    Thank you for the great video and lecture. It is really amazing, I have learned a lot from you. Anyway, do you mind also to share how to handle transactional is repository pattern and DDD perspective? Thanks again

  • @PiyushBhatnagar-h3p
    @PiyushBhatnagar-h3p 7 หลายเดือนก่อน

    Sir make a complete series on E-Commerce.

  • @bknbkn403
    @bknbkn403 3 หลายเดือนก่อน

    why the packages is not organized by business content but DDD concepts?

  • @mohamadmirmohamadi1215
    @mohamadmirmohamadi1215 ปีที่แล้ว

    How do you handle cycle dependency when some services calling each other mutually?

    • @programmingpercy
      @programmingpercy  9 หลายเดือนก่อน

      Cycle dependency was an issue at first for me in Go, but after a few months of go I stopped seeing that issue, its about a mindset.
      Services can be inside the same package, its the domains that should be separated and a Domain should preferably never need to import another domain

  • @yoashbentulila8638
    @yoashbentulila8638 ปีที่แล้ว

    why wouldn't you use a package for entities? and what would you do instead?

    • @programmingpercy
      @programmingpercy  9 หลายเดือนก่อน

      I am just not very happy with the Globally shared Entities approach.
      But I do it in some projects when an entity is shared across the entire scope of the project.
      I do like having entities as part of their respective Domain. If I am building a GitHub Domain, then the github related entities are found in that domain

  • @Flascoo
    @Flascoo ปีที่แล้ว +1

    Do you have a repo with this code?

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว +1

      Yes sir!
      The first video code can be found here:
      github.com/percybolmer/ddd-go
      Second video is focused on cleaning the structure up, since the first video uses a weird setup to emphasize the theory and ideas.
      That code is in a second brach in the same repo
      github.com/percybolmer/ddd-go/tree/clean-architecture
      I hope this helps you

    • @Flascoo
      @Flascoo ปีที่แล้ว

      @@programmingpercy Thanks!

  • @djgpp933
    @djgpp933 ปีที่แล้ว +1

    Cool explanation, how ever this is just crud, where will you add busines rules for instance, imagine there is a rule to not allow orders after 3pm or only allow to save only one beer by order I mean business rules not crud rules.. where will add those?

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว

      Good question!
      I'd add that in the Service or the Aggregates.

  • @dantesbytes
    @dantesbytes 22 วันที่ผ่านมา +1

    Dope 💩

  • @seriouscapitalist
    @seriouscapitalist ปีที่แล้ว

    do you live in paderborn or like all german houses look the same lol XD

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว

      Haha, sorry, I don't even love in Germany!
      I am living in Sweden, so pretty close though.

  • @klamberext
    @klamberext ปีที่แล้ว +1

    Peenuts.

    • @programmingpercy
      @programmingpercy  ปีที่แล้ว

      Haha yeah, my bad xD
      Also, a tip: don't ever order peenuts in a bar, you might get a real chocker

  • @jeromelanteri321
    @jeromelanteri321 ปีที่แล้ว

    interesting. At the very first start point, you exposed an idea about language use case to call same things differently with example by analogy as client to be also a drinker. By the facts, they are not the same thining of the same people as long as people doesn't react the same in different context.
    Ok, it is the same person id, but not the same idea of this person now. The bankster is looking for his money as a client, but the wine manufactor is looking at him as someone that will love his wine (the underground target is not the same, and because of that, depend of who is controlled the activity, the experience of the people will not be the same with a bankster and a wine manufactor).
    So, even if i do understand your point to teach domain driven design at the very first point, we can not delete that most of the time, people forget that they has been called differently because the target is very different and that is the power of a language to use the correct appelation depend of: "the context" (so, it is the school role to make student understand that crucial point that make a big difference between a dumb tool's guy society and an intelligent society). The context is the one than help to define the correct word to use to define a subject (it is lexicaly correct to understand this about language). Also, there is some "generic", that can embed all of them. For example, a client is a drinker or what ever that can paid for something, but all of them are humans (i think animals will not be accepted to drink or to paid for the drink too). It is all about to understand layer up to abstraction that exist due to context and motivated target.
    Let's said that this way we name things would also influence our own mind thinking the way we are thinking this topic about.
    And that is all about humans relationship and project orientation is. What i said is that... who is the boss ? Who is the one that should define the orientation of the project if not the owner of the project ? Is it the man that borrow the monnaie or is it the man that know to do the job and his the owner because of that ?
    Let me tell you an example that describe our low level society it is becoming right now because of genereic inplace of anything without conscience (because to not understand that point):
    More and more, now, in hospital, due to free international crazy market , people injured or seek are becoming: clients. And that is why they are not happy in this hospital who has no human skills as target at the very frst place. Peoople injured are no more happy due to the very low quality of treatment, and employed people are also not happy due to the treatment they have from the hospital that is becoming an other room for businessmen, instead to still be some valuable technicians, they become diplpomate personal, with lower and lower skills by the fact, with more an dmore pressure about time to treat a... client (or at least, a society of dumb that see only generic or there own business word to be the absolute priority, out of any cultural context, out of any politic context, out of any real life context and then, because of that, out of any valuable morality context).
    You can not make an adapter/wrapper between business and medecine or professional . Yeah... you can do it, but it is useless from the first start point to the end one.

    • @programmingpercy
      @programmingpercy  9 หลายเดือนก่อน

      First off, Thank you for watching and taking the time to discuss this.
      And I think that we shouldn't overthink this too much. It's all about finding common ground and a good enough abstraction that fits the model of the business scope.
      Although you have an interesting point.

  • @chenjaike950
    @chenjaike950 ปีที่แล้ว +1

    may be also need mutex to read map? here's chat gpt's example
    package main
    import (
    "sync"
    )
    type SafeMap struct {
    sync.Mutex
    Map map[string]string
    }
    func main() {
    // create a new safe map
    safeMap := SafeMap{Map: make(map[string]string)}
    // use the map in multiple goroutines
    go safeMap.Write("key1", "value1")
    go safeMap.Write("key2", "value2")
    go safeMap.Read("key1")
    go safeMap.Read("key2")
    }
    func (sm *SafeMap) Write(key string, value string) {
    sm.Lock()
    defer sm.Unlock()
    sm.Map[key] = value
    }
    func (sm *SafeMap) Read(key string) string {
    sm.Lock()
    defer sm.Unlock()
    return sm.Map[key]
    }