1

I was talking with a friend another day about OOP in small projects. In the most of projects that me and him worked the SOA was the rule.

Per example, imagine a Order in a SOA application. The scenario of this application could be:

  • A lot of services (UpdateOrderService, CreateOrderService, etc) calling each other.
  • The data of the Order is all open (lots of getter and setters) to be manipulated for any Service.
  • The business rules are distributed in many Services.

As Vaughn Vernon said in one of his books, this kind of strategy will not work well for bigger projects with more complex business rules. Many of us know that too.

By the way, SOA have a lot of different meanings and I'm taking the simple one, described by Vaughn Vernon: service classes calling each other.

The most obvious alternative is Domain Driven Design, right? But boy, this answer for a simple problem remembers me this phrase: "that escalated quickly". When you compare a simple SOA vs DDD, we are introducing a LOT of new patterns and complexity:

  • Unit of Work
  • CQRS
  • Aggregation
  • Domain
  • Subdomain
  • Mappers
  • Events
  • Command
  • Value Object

And etc. I already work in a big C# project using DDD. Was a amazing experience and opportunity for learn, but is not practical introduce all these concepts in a smaller project.

There is an approach called DDD-lite, but I can't find good or more detailed examples about it.

In DDD-lite territory, One of these examples not address one of the main problems that appears in some projects: use database entity as a domain object. For me this is a mistake, because is not possible to maintain the entity updated with the constant changes of the model and it will be mixed with another abstraction soon or later (like the use of VOs to represent some models). I see the entity only as a place to save/update/delete and search informations.

And, for me, this translation between database and domain is one the major challenges to create an OOP project. With all object associations and operations (create, update and delete), I couldn't find a simple way to introduce this in a project.

So, my question is: there is a midterm between SOA and DDD to introduce a OOP concept in the application without maintain them on the Services?

  • 2
    As soon as you start writing CRUD methods (UpdateOrderService, CreateOrderService), then you're either hovering just above the database (which is OK) or you're code is smelling. Having services do that seems like a code / design smell to me. Depending on your environment you can take steps to avoid it. – Peter K. Jan 23 '18 at 15:47
  • Before Eric Evans came along, this was considered ordinary Systems Analysis. The strategy is to learn your domain, gather requirements and then write classes that satisfy those requirements. A sensible class design helps. So does some basic architecture. – Robert Harvey Jan 23 '18 at 16:12
3

Yes, there is an alternative, and not only does it exist, it should be your default choice...

It's called encapsulating your business rules in business objects :)

The key to making a good business object is that it has no dependencies to anything, except perhaps, other business objects.

[Business Objects] <-- [Use Cases*] <-- [Data Access] <-- [UI]
*May not be needed for simpler projects
Arrows Show Dependency

Use Case classes can be used to model complex transactions that involve multiple objects.

A good business object will not have setters for its fields; its fields will be set in the constructor. It may have getters, but those getters should only be used to display information to the user and to save its contents to the database; getters should generally not be used externally to perform calculations.

The only way to modify a business object's fields should be through a method which represents an action that the class can perform.

Account account = GetAccount();
account.IsActive = false; //WRONG!!
account.Deactivate(); //Correct

In Data Access, there should be one repository per Business class. Repositories should have only CRUD methods that either take their associated object as an argument, or return it (or a collection of it).

accountRepository.Deactivate(accountId); //WRONG!!
//Correct:
account = accountRepository.GetAccount(accountId);
account.Deactivate();
accountRepository.Update(account);
  • But Account is a database entity, Am I right? – Dherik Jan 23 '18 at 18:09
  • 1
    No. That's what the dependency graph in the answer shows. The business objects do not know anything about the DB/ORM. – doubleYou Jan 23 '18 at 18:44
  • And how do you manage the translation between your Business Objects to/from database? Imagine that your Account can have another objects associated (Person, Credit, Debit, etc) and anyone of them could be deleted/created/updated. In DDD I already see a combination to Mapper/Uow/GUID/etc to take care of that, but this is a lot of work to do and more complicated that should be for a small project. – Dherik Jan 23 '18 at 19:14
  • @Dherik this architecture is the least amount of work, and the simplest! If you think this is too much work, wait until you try another approach ;) There are many ways to map the database records to the business object. Imo, the best way is, inside of the repository, use Dapper.net – TheCatWhisperer Jan 23 '18 at 19:27
0

What you describe is not particularly SOA (Service Oriented Architecture) but rather Transaction Script over objects as datastructures (the ones with lots of getters and setters). What OOP will bring you is thinking in terms of objects and the responsibilities they have, possibly encapsulating certain details along the way. A starting point could be as simple as putting methods/functions on the datastructures that capture the language and behavior they represent and only allow these to change the values of your datastructures.

  • thanks for the response. There are a lot of meanings for SOA, it's really confuse (google it and you will see). Vaugh Vernon, one of the DDD references in our area, said in one of his books the same thing and he picks the SOA meaning as some of us understand as "transaction script". – Dherik Feb 19 '18 at 16:20
0

A lot of services (UpdateOrderService, CreateOrderService, etc) calling each other

Am I wrong for thinking you are overcomplicating things right from the jump for a "small project"?

I feel like we often have patterns in mind at the start of projects and leap into using them right away, sometimes inappropriately.

For a small application, why not simply start with just what you need? Namely, a single service to hold all your transactional methods, with the business logic located in that service as needed, and the service methods returning basic data structures to the various clients to display/manipulate and send back into the service for basic CRUD and command and such.

  • Thanks! I'm looking for a solution where my simple application can evolute for something more OOP. – Dherik Feb 19 '18 at 16:22
0

The Quick & Dirty Architecture (Not recommended for professional projects)

The solution I am about to propose has problems, and I would not recommend it (See my first answer). Problems with it can include overly large source files, and inability to scale persistence performance.

However, it is simple and quick to implement with only two layers and still encapsulates and organizes business logic. Additionally, I would say it is better than a lot of what I've seen pass as architecture in the wild... Using ORM entities sprinkled throughout the applications and "Repositories" with business logic are surely worse.

[Business Objects (With Data Functions)] <-- [UI]

A typicial business object would look something like this:

class Account
{
      // Descriptors:
      string Name { get; set; }

      bool IsActive { get; set; }

      decimal Balance { get; set; }

      //Business Logic:
      void Deactivate()
      {
          this.IsActive = false;              
      }

      decimal CalculateOwedTax()
      {
           return this.Balance * TAX_RATE;
      }

      //Persistence
      static Account Get(int id)
      {
          return DB.Get<Account>("select * from account where AccountId=@id")
      }

      static IEnumerable<Account> GetAll()
      {
          //DB CODE
      }

      static void Create(Account account)
      {
          //DB CODE
      }

      static void Update(Account account)
      {
          //DB CODE
      }
 }

Note, it is very important that the persistence code are in static methods, this is not an accident. You will not see any saving code in the instance methods, which are dedicated to encapsulating business logic.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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