How To Structure Domain Driven Design (DDD) In Go

Поделиться
HTML-код
  • Опубликовано: 28 сен 2024

Комментарии • 21

  • @woytecki
    @woytecki 2 месяца назад +3

    Man, this series is better than a course I bought for hard cash. Great stuff!

  • @AlexGhoro
    @AlexGhoro Год назад +2

    Thanks for this serie very useful!

  • @Sorm83
    @Sorm83 Год назад +7

    Great job! Can you perhaps make a video on how to organize code with DDD and Hexagonal Architecture? I know this is all opinionated but still. For me it is a challenge where to put dto and dao as they definitely outside of domain and are usually tagged (json for dto and db/bson for dao) , separate ports and adapters, domain and infrastructure ... where to put middleware, jwt services, loggers, clients reused by multiple repositories, own error packages etc.

    • @programmingpercy
      @programmingpercy  Год назад +7

      I will put it in my backlog for sure, more than you have been asking for it so it's a good idea!
      I cannot promise a release date as of yet though. But it is brewing

    • @maydersonmello
      @maydersonmello Год назад +2

      @@programmingpercy That would be amazing, it will help a lot to get a sense of how and where to apply in the application.

  • @xxmmcc-ck2yr
    @xxmmcc-ck2yr Год назад +2

    Hello ~
    If i want to send domain events, where should i place the code?

  • @Suraj-tz3oq
    @Suraj-tz3oq 28 дней назад

    What if I have multiple services and one service need to go into other and the database is not same in those. do we have to start from top level with less dependency and add that to dependent services later and make some kind of a big file or there is some shorter way to do it?

  • @HannaBrasch
    @HannaBrasch Год назад +2

    hey, can you give an example for the billing service?
    My thought was to pass the customer in as a pointer (fetch the customer from orderservice in the createOrder methodof the tavern) and add the amount the transaction valueobject, but the transaction wont be attached to the customer object created in the test case.
    2nd: There is a lot of boilerplate code within the configure function pattern. How can someone solve it?
    3rd: how to handle situations where the attributes where the configuration pattern is apllied are nil? Currently the application just crashes and nil checks everywhere are no real solution.

    • @programmingpercy
      @programmingpercy  Год назад +1

      Great points!
      I will see if I can push a solution to the billing service this weekend or the coming week.
      2. Yes, each service tends to get a little boilerplate with this cfg pattern.
      It's a trade of, I like the modularity it provides.
      Me specifically, I use a Vsnip for both my VSCode and NeoVim setup to generate this boilerplate.
      3. One approach that I might have failed to mention is the validation, one could have a validation func that makes sure the least valid setup is available, this func would be called upon after iterating through all the cfgs.
      This is probably the easiest way, though it does contain nil checking each instance.
      Not sure I agree nil check is not a valid solution.
      A cfg validation could be its own func on the struct, or a passable cfg of its own.
      Personally, I haven't had the time yet, but I think a lot can be improved here with generics

  • @kaigu1322
    @kaigu1322 Год назад

    👍👍👍

  • @idfumg
    @idfumg Год назад

    I think we should split the responsibility of knowing about different kinds of databases (in-memory, mongo, pg, etc) into different modules In the order.go. In the current representation your order knows everything about different databases. Which is violates SOLID principles. It should receive only the interface. In the ultimate version it should implement its own interface, so you will not be tied on the interfaces of different domain specific types (it will allow us to satisfy dependency inversion principle). But, it will lead to much more code then. DI can be kind of tradeoff. Also, You violate DRY principle by copying that init_products function everywhere. It should go in some separate utils package.

  • @farzadmf
    @farzadmf Год назад +4

    One comment: `customer.NewCustomer` also "stutters"; recommended to have `customer.New(...)` 🙂

    • @programmingpercy
      @programmingpercy  Год назад +4

      I totally agree!
      My miss, let's agree that reusing the package name in the function names is almost never a good idea!
      Thanks for letting me know, I'll modify the Repo

    • @farzadmf
      @farzadmf Год назад +2

      Thank you for the videos; this was just a minor comment. Keep up the good work

  • @Suraj-tz3oq
    @Suraj-tz3oq 28 дней назад

    Also how to inject loggers and configs into all code

  • @KiVo16YT
    @KiVo16YT Год назад +1

    Really good series, but I've got an issue that I can't get my head around. You have products property in your Customer aggregate but don't do anything with it. When an order is created the product slice is not even attached to the customer and Mongo implementation doesn't even have any handling of products attached to the Customer aggregate (Repository in customer module). Let's say I want to get all items of specific customer (potentially to calculate total amount and charge them for ordered items) and use Postgres - probably every ordered item should have a foreign key that relates it to the specific customer. Also, let's say that the Customer can add specific item and then can increment its count value 1 -> 2 -> 3 ->, etc (the first "add" creates records, and later on only the count value is incremented). How should I approach that? How the item of a specific Customer should be updated? 😄
    Besides that don't you think that creating Setters and Getters for a struct isn't an antipattern in Go? It creates a lot of boilerplate code like e.g in Java. Isn't it something that Go creators wanted to avoid? Maybe simple validation at the end of every endpoint/handler would be sufficient and it would allow direct access to struct properties. I understand that making fields private prevents other developers to use your code incorrectly but readability suffers in my opinion. What is your thought about that? 😃
    One last thing. Let's say I want to switch from Postgres to Cassandra. In that case, using only 1 value (uuid) to identify a record is not enough (probably a similar thing with sharding SQL database). If you want to efficiently query data and be able to split it into multiple partitions then you have to use some kind of key that corresponds to a specific set of records - e.g. if we would have multiple taverns then part of the primary key should be at least tavern_id and just id (probably also some kind of month or year to prevent the partition from overgrown - might take some time but we should at least think about it). I once tried to abstract ID with the interface and make every repository implementation to also implement some ID resolver/parser that can get tavern_id and id from a string like taver_id_1:id_2 (both ids can be uuid, literally anything). What do you think about this approach? Maybe you have a better idea?

    • @programmingpercy
      @programmingpercy  Год назад +1

      The unused products field is a by product of this being a tutorial, this is not a complete project that solves all issues and does not sthrive to be.
      The items are left unused because there simply isn't much reason to cover all aspects, as the aim is focus on DDD.
      There are many other things unoptimized/weird in this project, but that's not really what the project tries to showcase.
      About the Antipattern, I agree that it is considered an Antipattern by the Go team to use Getters and Setters.
      It's totally acceptable to have setters on private fields Only, and leave public keys just using capitalization.
      And I have conflicting thoughts myself about it, so here is a very political answer, it depends on the use case!
      Normally I avoid setters and getters but it depends on the use case, If values are allowed to be changed and doesn't require any specific logic, then sure, use the Capitalized public variable.
      If it's passed back as an Pointer and maybe used else where, then I'd start thinking about the need for a setter.
      As soon as custom Business logic is needed for any change, then setters are fine.
      I'd love if we had a better way of having non modifiable variables that was public, like in Rust where you can assign an Mod property. Sadly we don't have a good way of doing this in Go, a public variable is modifiable, so setters tends to be used for this case, atleast by the teams I've been in.
      For the UUID part, I am sorry But I am not sure I follow why it wouldn't work across shards and multiple taverns? Care to explain this more?

  • @РоманСарваров-ч5л
    @РоманСарваров-ч5л 8 месяцев назад

    Why cant we move services to domain folders as well?

    • @programmingpercy
      @programmingpercy  8 месяцев назад +1

      I tend to view the services as a business logic solver. It binds together different domains to solve an bigger issue.
      I like to keep my domains very Niched, say we have a Weather domain.
      It's core use is to fetch weather data from different sources, and that's all it does.
      But our Service is to tie the weather data to Insurance reports of property damage.
      The service will contain and bind many domains to solve an issue.
      That's why I usually keep my services outside of the Domains.
      Domains: niche usage for single purpose
      Service: combine domains to solve a bigger issue that relies on multiple domains.
      At least that is my take on it. Hope this helps you.

    • @РоманСарваров-ч5л
      @РоманСарваров-ч5л 8 месяцев назад +2

      @@programmingpercy ty!