ActiveRecord

Summarized using AI

Business Logic in Ruby

Andrzej Krzywda • March 22, 2019 • Wrocław, Poland

Business Logic in Ruby: A Journey Through Programming

In the presentation titled "Business Logic in Ruby" by Andrzej Krzywda, given at the wroc_love.rb 2019 event, the speaker delves into his programming journey, emphasizing the handling of business logic within Ruby applications. Krzywda shares insights from his transition from Java to Ruby, highlighting the differences in approach to coding and business logic representation in both communities.

Key Points:

  • Introduction and Background: Andrzej Krzywda introduces himself as both an organizer and speaker, expressing gratitude for the turnout of passionate Ruby programmers. He mentions his background in Java, particularly in Agile methodologies focused on coding quality and business rule expression.

  • Transition from Java to Ruby: Krzywda addresses the initial excitement Java developers felt with the emergence of Ruby and Rails because of their elegant syntax and emphasis on testing and quality. He reflects on early adoption of the ActiveRecord pattern in Rails, which simplified database interactions but introduced complexities.

  • Business Logic and Code Coupling: He discusses the problem of tightly coupling code to the database when using ActiveRecord, which can lead to hidden complexities as the application grows. He emphasizes the importance of separating the reading and writing responsibilities to maintain clean code.

  • Proposed Approach: Krzywda suggests splitting ActiveRecord into two components: one for writing operations (focused on implementing business logic) and one for reading operations (dedicated to displaying data). This separation helps alleviate excessive coupling and simplifies the codebase.

  • Implementation Example: He provides an example of how refactoring can lead to clearer code without ActiveRecord dependencies, implementing mechanisms for raising exceptions and handling events.

  • Domain-Driven Design (DDD): The speaker briefly introduces Domain-Driven Design principles, referring to business logic classes as aggregates. He mentions discussions within his team on effectively implementing business logic in organizational contexts, such as tracking issues with systems like JIRA.

Conclusions & Takeaways:

  • Krzywda underscores the significance of clear separation between business logic and database interactions, which enhances code clarity and maintainability.
  • He advocates for the pragmatic application of Domain-Driven Design to address complex business logic requirements when working with Ruby applications.

Overall, the talk provides valuable insights into the evolution of coding practices from Java to Ruby, highlighting the importance of managing business logic effectively within the codebase.

Business Logic in Ruby
Andrzej Krzywda • March 22, 2019 • Wrocław, Poland

wroclove.rb 2019

00:00:14.650 All right, I think I'll just introduce myself. My name is Andrzej Krzywda. Today I am in two roles: I am one of the organizers here, and I will also have my talk. As an organizer, I would like to welcome all of you. I am very grateful and happy that you have come here and that we can meet with so many passionate Ruby programmers. I think this shows the power of Ruby. Thank you very much for coming. My talk is kind of like a story about my journey in programming—my hobby, passion, and programming career. I want to show you how I think about code, particularly business logic, and I would like to present some examples of the code. I would also like to invite you to share some of your ideas with me.
00:00:44.170 Just to give you some context, I should warn you that the next slide might be offensive in the Ruby community because it contains Java. This is my coming-out moment: I was a Java developer and a part of the Java community before I started doing Ruby. I was involved in a specific part of the Java community that focused on Agile, but Agile 20 years ago didn't mean meetings; it meant caring about the code and expressing business rules in the codebase nicely, along with unit testing and so on. It was really a code-specific focus. I'm mentioning my Java background because it had a significant influence on my approach, so with that warning in mind, let's look at some Java code.
00:01:24.730 Java, can anyone tell me what this code is about? It's an implementation of a post class, similar to what DJ has shown in the initial Rails applications. What’s specific here is that this Java code doesn't use any framework or library. In the Java community, we call those kinds of classes 'POJO'—plain old Java objects. This concept was essential in the Java community because the idea was to have business classes that were not bogged down by framework extensions. Twenty years ago, there wasn't much in terms of frameworks in programming communities; it was more about libraries, and it was considered a design smell or a bad practice if your business classes imported any outside extensions. Business classes were meant to be pure and free from any external library influences.
00:02:18.489 When Rails appeared in 2004, a significant part of the Java community, including myself, was excited. We were drawn to Ruby because the community was deeply focused on testing, unit testing, and code quality. The language's syntax was so elegant that it felt like we wanted to write Ruby in Java all the time without realizing that Ruby even existed. However, when the Rails community enticed us with its features, we were also unknowingly adopting some challenges. One of the features that many of us accepted was the practice of inheriting from ActiveRecord base. When DHH showed this code, which was shorter than traditional Java, we thought, 'Oh, this is better!' Now, our communication with business requirements and business people became easier because they could even look at the code and somewhat understand it, apart from the ActiveRecord base part.
00:02:46.930 However, what DHH very nicely concealed from us was the fact that this code does have some hidden complexities. This is what happens when we inherit from ActiveRecord: we have readers and setters, a constructor, and eventually we end up with tightly coupled code to the database. This was indeed a big deal for initial Rails developers, as it seemed so easy to use with the database already part of ActiveRecord. We embraced it, thinking it was not a significant issue but instead a great feature. But then we gradually realized we could write even nicer code. For instance, if a post has many comments, we can express it that way. The validations are incredibly simple to implement, and we were excited that everything was there and working. However, we didn't realize that we were tightly coupling ourselves to those objects. Each ActiveRecord object poses complications, especially when they grow and become complex.
00:03:46.510 This leads to one of the biggest realizations of my journey. I don't want to dive too deep on separating reads and writes, but my next topic is focused solely on business logic. This means making decisions directly in the codebase. I do not want to be concerned with readers—this is what ActiveRecord is about; it has two roles: one for writing data and one for reading it. Writing data involves making decisions—do we accept this post as a new title for the blog post? If validations are passed, then it's fine. We accept this. Reading data involves using associations, such as has many comments, which are usually used for displaying information later, rather than making decisions. You don’t concern yourself with changing the title or knowing the count of comments when you're not making decisions based on that input.
00:04:14.560 With has many comments, you now introduce a significant dependency and coupling to comments. As Marcus, who conducted a mutation testing workshop, once told me, ActiveRecord has an infinite API. Once you inherit from ActiveRecord base, your model is endowed with a plethora of available methods, leading to a vast interface. How can we alleviate this issue? One approach is to split ActiveRecord into two objects: one focused on reading operations, where readers would not be part of it, and the other concentrating on writing operations. This way, we only retain our business logic without the extraneous dependencies or coupling that comes with it. The idea is to introduce read objects purely for the purpose of displaying data.
00:08:24.740 Now let me show you how this approach would manifest in code. You start with the initial implementation and then layer the business logic into separate objects. Note that there is now no ActiveRecord dependency, and you can implement logic, such as raising exceptions when a post does not have a title. Additionally, we need mechanisms for publishing events since that's the only way these two parts can connect seamlessly. The rich object can then accept published events. Employing such a method, we can retain ActiveRecord’s advantages for data display purposes while mitigating coupling, allowing us to retain clarity and separation of concerns throughout the codebase. The algorithm of splitting responsibilities leads to cleaner, smaller ActiveRecord models by avoiding the excess baggage caused by read configurations.
00:12:30.780 To explore more complex scenarios, our team conducted extensive discussions over the years on how to implement business logic effectively. Following many conversations, we adopted Domain-Driven Design (DDD) principles and referred to our business logic classes as aggregates. This talk isn't about DDD specifically; however, during our race architect masterclass, we developed a repository internally at our company. This repository, created by my colleague Pavel, focused on implementing different business logic implementations—from discussions to practical applications. We ventured into how aggregates might look and whether specific requirements exist for various facets of business logic implementation. I also discovered a crucial requirement that now guides our approach to non-trivial situations which includes examples pertinent to commonly-used structures like JIRA. Implementing business logic and state machines often aligns closely with practical organizational tasks like tracking issues and their lifecycles through different statuses.
Explore all talks recorded at wroclove.rb 2019
+9