1

I am having a bit of problem trying to find the correct place for business logic. The project I am working on isn't huge, but is not small either and has a very considerable importance in my company. That said, I'd like to know how to better organize my business logic. First an example of how things are today.

The method below is inside a service and is used to confirm a negociation. The method is used inside another method (newNegotiation) in the same same service, and all the method it calls are private. I picked specially this method because it has something inside of it that is the point of this post: the $this->confirmCart();. Also, the service is used inside a controller, as shown below.

  public function acceptNegotiation()
  {
    try {
      DB::beginTransaction();

      $this->getNegotiationData([4]);
      $this->generateIngressosCodes();
      $this->confirmCart();
      $this->registerConfirmedLog();
      $this->sendConfirmedNegotiationEmail();

      DB::commit();
      return ['status' => true, 'msg' => "Negociação confirmada!"];
    } catch (\Throwable $th) {
      DB::rollback();
      return ['status' => false, 'msg' => $th->getMessage()];
    }
  }

The service being used inside the controller:

public function createNegotation(NegotitationRequest $request)
  {
    $this->funilSellService->setData($request->all());
    $response = $this->funilSellService->newNegotiation();

    if ($response['status']) {
      return response()->json($response['msg'], 200);
    } else {
      return response()->json($response['msg'], 400);
    }
  }

So, about the $this->confirmCart() (as I said, it's a private method inside the service), but it just feels wrong to be called that and to be a method of a service. Thinking of the principle "tell, don't ask", it gets even weirder, because in this case, it seems better for the Cart to update itself: just tell it to (something like $cart->confirmPayment(), where the $cart is an instance of a model Cart). But this ideia of mine seems wrong when thinking of a model as a way for transfering data from the database to the rest of the application. If I go on with this ideia of placing business logic inside the model, I might end up having logic and a transfering layer mixed up.

As you can see, the slim controller seems fine, but the problem falls on where to put the business logic, since the service is coordinating some functions and the model should act as transfer layer, as far as I've read, differently from a Domain model (but I don't know if I should be using DDD with Laravel). Anyways, where to put the business logic?

2 Answers 2

4

We’ve learned over the years that most small projects grow over time. What starts as simple CRUD applications evolve into business critical systems and complexity increases exponentially. Sooner or later a feature request comes in that requires a full rewrite.

Separating domain models from database models seems like a lot of additional effort initially, but it comes with many benefits. One of the biggest, in my experience, is that it’s very easy to test the domain model in isolation, because a good domain model has no dependencies. Something that’s easy to test, is also easy to change!

So as a concrete answer to the question ‘where to put the business logic?’ my advice would be to place it in a separate module that does not depend on anything. Not even repository interfaces, because that’s still a dependency on the database.

1
  • That's a very nice take. Actually, this very project I am talking about is not huge, but has grown already. Also, what, exactly should those separte modules be? I am working with Laravel and because we used the MVC structure since the beginning, moving to DDD is a bit complicated. But any suggestions are welcome. Jul 26, 2023 at 19:37
3

Not sure if it will help with the question, but I have a few qualms about your design:

  • When I see "Service" I expect something more or less stateless. With or without a domain-driven approach. This is not the case here - we seem to have some kind of business transaction (OK) that relies on data fields of the service object having been initialized before (not really OK).
  • The business side of things could be made clearer through better naming. FunilSellService seems to deal with Negotiations. But since it's not named NegotiationService, I assume it deals with other entities as well?
  • What is the difference between CreateNegotiation and AcceptNegotiation? CreateX... can be either purely technical (e.g. instantiate an object) or have a business meaning. I don't really know which is which here, and the absence of a clear domain layer doesn't help.
  • You lost me at $this->confirmCart() and $cart->confirmPayment(). What does it do? It seems weird to hide the responsibility of finalizing the payment transaction into a shopping cart object, without any control over how that goes other than exceptions.

Based on your snippets alone, I would say the code is complex enough to deserve a separate domain model. Especially, the concept of Aggregate Root from DDD might be useful in your use case. But maybe you can show the code around to colleagues and ask them if it's readable and simple enough right now. Being able to understand a piece of code in a glimpse is an important aspect of good design.

4
  • 1) Why "not ok". 2) It deals with negociations that are made on another platform (Funil) and then registered on my one. 3) CreateNegotiation is a method inside the controller while AcceptNegotiation is a method in the service. 4) It simply updates the record on the DB for that cart (a field that indicates the current step of the cart). And yes, it doest seem weird to finalize the payment transaction, that's kinda my point. Also, I don't know much about DDD, because I work with Laravel and it works as an MVC; but only using MVC seem too "weak" for that. Jul 26, 2023 at 17:02
  • 1) Having operation-dependent state inside a service blurs the line between service and business model object. It prevents your service from being used in a longer-lived scope such as singleton. The Laravel code samples I could find out there involve stateless services. Jul 27, 2023 at 7:36
  • MVC and DDD (or rich domain model) are orthogonal. MVC is a presentation layer pattern. A lot of applications use MVC+ a rich domain model or DDD patterns. Jul 27, 2023 at 7:39
  • That's nice to know I can actually mix DDD and MVC. Thanks. Jul 27, 2023 at 11:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.