Architecting an ecommerce application following Designing Elixir System with OTP

Context:

I have completed chapter 3 (data), chapter 4 (functions) and chapter 5 (testing) of Designing Elixir Systems with OTP. And i’m currently building an e-commerce platform with the following functionalities:

  1. User can create an account
  2. The authenticated user can add products to their cart
  3. The user can checkout their products from the cart
  4. The user receives an order confirmation

Questions:

Here is a quote from the book explaining a bank as a data

It’s an initial balance plus a set of transactions at a point in time. These transactions are functions.

  1. In the context of an ecommerce application what are the initial “balance” and “set of transactions at a point in time” do I need to keep a track off for: users, products and orders?

  2. In the context of an ecommerce CMS application what are the initial “balance” and “set of transactions at a point in time” do I need to keep a track off for: users, products and orders?

1 Like

I feel like e-commerce makes for a great example of what we were describing in the book, because common ways to build the models can easily become problematic.

Can you still show accurate historical orders when a product is removed or goes up in price? Find data structures that can represent change over time and you’ll be in a much better place.

First off I want to thank Bruce and you for writing the book!

Can you still show accurate historical orders when a product is removed or goes up in price?

This is what I am having trouble understanding, because the data structure would be an ecto struct. As a result, all the changes will take place within a controller and a new state will get saved to the DB. For example, the admin changes the price for product A. The controller, will make an updated to product A and save it to the DB.

My question is, how do we work with DB as the structure and still capture the set of transaction?

For me, it often helps to put the database aside for a moment and figure out what data structures I really need. Once I have that, then I can ask myself how I would save it to a database.

Does that help get the ideas flowing?

7 Likes

As @JEG2 mentions you need to figure out what data structures you need in your application (maybe you don’t know them all at first hand, but will arise according to your needs). Its ok if your DB struct differs from your transactions.

I’m not following the book but until very recently I have been looking into “event driven design” and this talk about the commanded library plus following the github guide helped me a lot into better understanding this mental model.

Note: You don’t need to fully buy into commanded’s mental model; you can discern your system’s primitives without it, but I hope it can give you another angle to view your problem from.

1 Like

This make sense! And that’s exactly what I did during my data discovery process. However, I’m unclear about the user data structure. The question that I’m trying to answer is, what type of transactions do I need to record for a user data or is this even necessary?

I also noticed that in the book user data wasn’t captured. Was it left out because its data wasn’t significant for the quiz application?

@chouzar thank you for mentioning CQRS/ES. After having read this article and watching a couple of conference talks, I believe I am able to answer my question

what type of transactions do I need to record for a user data or is this even necessary?

For the content management aspect of the ecommerce application, I don’t need to record any events for user data, a simple CRUD is sufficient enough.

We didn’t include users because the example is already complex enough and everyone shows users. We figured readers could tie that into their own designs.

I agree that simple CRUD is probably fine for your user models. I think the real trick is in getting products right.

3 Likes

I agree, I was also thinking of using CRUD for products as well. Since, features like

isn’t required for my application.

Thanks for sharing the article! One of the things that really worries me into going all in into a cqrs design is the feeling feeling that I have to get my entities “right” the first time.

A change that affects one of my “immutable” stores sounds like maintanability hell. Althought you could have a case that its easier to progressivelly enhance your architecture :thinking:

I had the exact thought as well. I don’t want to introduce complexity until it’s really necessary.

This is the place that James and I always came back to throughout this book. “Let’s see how far we can get without persistence.”

We found that thinking about the solution in these terms shaped the way we thought about the problem.

4 Likes

@redrapids and @JEG2 are there more sample repos that you can share with the community to get a better grasp of the concept taught in the book. I love to see the principles applied from multiple angles.

1 Like

The following is an early example of me playing with deferred persistence:

1 Like

I would say the ideas are more common in Haskell and other functional languages. The idea of supporting a full functional core and handling boundary concerns with callbacks is a popular one in other languages, but not as much in Elixir.

I do see that James hooked you up. I think that he did a talk or a training several years back. Also, Brooklyn Zelenka has a bunch of talks about this general idea. It’s not exactly about this concept, but she gave a talk at CodeBeam SF that was out of this world, and you should look at how she shaped the core code and used her witchcratt library to wrap it.

Sorry I can’t do better. It’s really a good question.

-bt

First of all, big thanks to @JEG2 and @redrapids for the awesome book. Loved it!

I’m also struggling with the question of when, where and how to introduce persistence.
Especially when working with a rather simple and traditional CRUD model, like users for example (sorry for bringing this example back up), where there may only be simple persistence and query-related operations, the functional core (if any) would probably end up being very slim and all logic would end up in the boundary layer.
In this particular case, I would intuitively use an Ecto schema to model the data, but I feel like that defeats the purpose of “Do Fun Things” and might even introduce a lot of cross-context shenanigans especially if the data is of rather relational nature.

My question is, how would we build a functional core if most use-cases depend on persistence, simple CRUD and query operations? (I think this also works for the ecommerce example.)

1 Like

I’ll make a few general comments:

  • If a traditional CRUD design is meeting your needs, it doesn’t sound like you have any problems that need solving
  • However, I don’t think of users—the most likely “god object” in any system—as simple
  • And I suspect you can learn something by trying experiments where you add persistence as you would any other feature (because I definitely have every time I’ve done so)

But yeah, don’t overthink it. If you don’t have a problem, there’s no reason to change your design. (Except to run experiments to learn new things!)

3 Likes