00:00:10.390
Thank you very much. A part of my talk is about handling failure. We had a pretty good example so far, and I hope everybody remains calm. Thank you! Hopefully, my talk will cross paths with some of the things I watched today.
00:00:21.529
I didn't make my honeymoon in Florence yet, and I used to be quite subject to a lot of uncertainty. So, everybody seems to be very cautious as well. Just like Xavier, I'm very cautious too, and I won't provide any conclusions yet. I'd like to give a state of the art overview of what we find in the Ruby community.
00:00:38.329
Maybe you'll recognize some of the techniques you use. Of course, I have my own opinions, but we will explore various techniques.
00:00:52.370
To start, let's discuss structure and how to chain your services. There’s a lot of terminology used in programming, and it can be very complicated. The term 'poor' is often used, even if it doesn’t have any real meaning.
00:01:04.820
The main use case for service objects is to encapsulate logic. Simoni just provided a great example before. The popular use of service objects is in application service operations, commonly referred to as operation comments or retreat pattern objects.
00:01:11.930
In this talk, I will refer to them as service objects, and I hope you will follow along. So, to define a service object: it is supposed to show what your app does. It's designed to express your business logic and is framework-independent.
00:01:35.600
A service object is specialized, and because of its name, it carries context. For example, you could have a service object for 'create user', which indicates that every time you create a user, you may need to send an email or perform other actions.
00:01:43.670
So, it can handle conditional validations and callbacks. It serves as a wall in itself and is very convenient. Service objects have been developed and debated for years, and it's really not a new concept. There are countless gems available; you might be using some of them, and they apply different strategies.
00:02:07.989
I couldn't resist creating my own, which is at the bottom of the list, but I know there are many others. There are even books on the subject. For example, Trev laser is quite a trend these days. Nick told me that you don't need a coupon because the 'trespasser' book one is free.
00:02:27.400
However, I do have a coupon for the 'Fearless Refactoring' book if you're interested, just let me know afterward. In terms of service objects, there are topics that are pretty much settled, while others are still much debated.
00:02:50.000
Let’s start on the same ground and talk about what is generally accepted regarding naming, which is often a pleasant surprise. Naming is indeed always complicated, but having fewer public methods can simplify this.
00:03:04.820
In service object naming, we typically use verbs since they are meant to perform actions rather than represent something. Your service object can handle data representing something, but it primarily performs actions.
00:03:23.300
The public methods are generally minimal, allowing you to trigger the object easily. We'll look at the various techniques I've observed, which really depend on the flavor of service objects you choose to utilize.
00:03:41.540
Service objects are designed to be reusable. For instance, if you have service objects to build, they should be usable right out of the box in your specs. There’s no reason to create objects differently from the rest of your application.
00:03:53.000
What everyone does on your website or application results from different actions, which are managed by your service objects. You could even configure a setup where you invoke your service objects sequentially to achieve results in a realistic environment, unlike a fantasy land of convoluted steps.
00:04:10.000
This method has its costs, as there may be many database actions involved, but that’s an important consideration. The main disputes arise around how to trigger actions, whether or not to coerce arguments, the structure of responses, and failure handling.
00:04:25.240
Let’s zoom in on triggering these actions. You have class-based approaches where common methods like 'call', 'run', or 'perform' are used. The alternative is instance-based, where you pass arguments to either the new instance or the performing method.
00:04:47.730
The choice really is up to you. There are various trends; I don't personally use them, but I know others find them useful for good reasons. There are several gems developed for this purpose; some of the most popular tools rely on service objects.
00:05:01.600
In Rails, there are countless ways to structure service objects due to how constants are loaded, leading to potentially strange cases. The first method is to create a services folder and have redundant service names for each class, though I do not prefer this approach.
00:05:12.830
Another option is to have a service named simply, like 'CreateAccount', which works well. The Trailblazer approach has its own intricacies, as Rails may not correctly locate the path if certain conditions aren’t met.
00:05:31.510
You could also create an empty folder within the services folder to ensure that Rails recognizes it, despite this method seeming absurd. It’s all about utilizing namespaces effectively.
00:05:48.700
In terms of responses, there are plenty of options. You could design a service that responds with a boolean: 'success?' which indicates if an operation succeeded, or have it render a response object that stands independently.
00:06:10.950
There could also be additional public methods, which can expose what was accomplished, such as success or failure states. Conversely, some services will have no response at all, which can be more straightforward.
00:06:29.980
Previously, we talked about being optimistic in Ruby, and that's okay. However, we need proper flow control as we implement this in our code. For example, in a service, you might create a new instance with arguments and call a public method to access the necessary data.
00:06:42.500
It's simple, yet it relies on uncertainty because you don't know what might occur during this process. Introducing an exception implementation can make things clearer. The structure can remain similar, where you'll call your service.
00:06:59.000
Based on the response from an external service, you will either return the data or raise an exception. Opinions on exceptions in Rails vary widely, with some suggesting they should be avoided due to performance considerations.
00:07:15.000
Others argue against using specific exceptions if they lead to complications elsewhere in the application. Ultimately, it’s a highly debated topic—with no definitive truth, as opinions differ significantly.
00:07:30.000
In this implementation example, you call the exception service, and if everything goes smoothly, you handle the desired path; if not, the rescue block comes to your aid.
00:07:48.000
Here’s the boolean implementation of the service. A success instance variable indicates whether the operation succeeded. If it did, you can execute a certain action; if not, you'll take alternative steps.
00:08:03.000
This raises the question of how to implement error handling effectively within your services. We'll explore various implementations of error handling throughout our examples.
00:08:12.720
For instance, there are event-based services, such as the popular library Whisper, which is widely used for broadcasting events. In this case, you'd have two paths: the success path, broadcasting a successful outcome, and the error path, providing the reason for the failure.
00:08:27.230
To use it, you'd create an instance of a service, invoking methods for success or error in the corresponding cases. On calling the service, you can execute specific blocks based on successful broadcast or errors.
00:08:39.300
Another approach involves using monads to manage success and failure. In Ruby implementations, this typically looks like returning success or failure objects, executing different blocks of code as appropriate.
00:08:51.720
However, it’s essential to be cautious with their syntax, as you must return a confirmation for success or failure to correctly navigate the chaining process without causing unintended issues.
00:09:05.000
In conclusion, while the various implementations may differ, the principle of structuring your service objects to manage success and failure effectively remains constant. Achieving proper chaining helps ensure your application behaves as intended.
00:09:20.000
Chaining helps maintain efficiency and error visibility within the application. Each service step could either fail or succeed, and you need to ensure you understand why it happened. Proper chaining means failing efficiently and ensuring that error handling is coherent.
00:09:51.600
It’s essential that in your implementations, you bear in mind the importance of success and failure paths to correctly handle outcomes. This control allows you to maintain sanity within your code.
00:10:02.250
With regards to the exceptions earlier mentioned, the main setup allows services to capture useful data and ensure clear parameters for success or failure outcomes. When properly structured, the resulting implementation can manage failures gracefully.
00:10:23.740
If you're implementing boolean checks on service operations, this can lead to tricky situations in your code. Effective error-handling maintains clarity and simplicity.
00:10:33.230
Let’s review the monad functions as part of our exploration. Although it can get cumbersome, especially when the services become interdependent, maintaining a logical and organized structure goes a long way.
00:10:46.000
You’ll want to ensure that added complexity through chaining doesn’t create confusion. This can be done by ensuring that failure handling is consistently implemented across all service objects.
00:11:09.000
As we conclude, remember that implementing effective service objects takes time. These objects can help simplify your architecture by isolating business logic and making it easier to test and manage.
00:11:20.000
In essence, any solution you choose, provided you shape your approach around service objects, contributes to a better architectural framework. The principles and layers you apply can significantly impact your workflows and ease of maintenance.
00:11:34.000
Once your business logic is appropriately isolated within service objects, you can better test it and manage changes. This isolation leads to cleaner architectures that can evolve without compromising the core functionalities.
00:12:04.000
It's important to recognize that your choice of architecture, whether it aligns with certain patterns, serves as a facade, helping keep your core logic intact and consistent.
00:12:27.000
Feel free to ask about event handling, as it's a common topic. When you use events solely for success and error handling, you might inadvertently create callback hell, where callbacks become unruly.
00:12:41.000
If you have specific requirements, such as creating events based on particular instances, you can structure them more effectively. My general recommendation would be to use dependency injection where necessary and apply best practices for modular design.
00:13:04.000
JavaScript, for example, has tools designed to assist in these contexts. When responding to HTTP requests, ensure you prompt users efficiently; prefer a quick response over prolonged waits.
00:13:19.000
Fail fast in situations requiring quick responses, and delegate longer wait processes to worker engines.
00:13:26.000
Red laser—though I don’t use it directly—offers valuable design patterns, and if you need alternatives in your coding journey, be sure to explore service objects thoroughly.
00:13:46.000
A service object is useful for sign-up forms where you need to validate a set of fields like names and emails. When time presses, using proper forms keeps your application data valid.
00:14:10.000
Design patterns provide structured approaches for various needs in programming. Service objects, for instance, can embody commands and often simplify interactions.
00:14:27.000
Not all design patterns are created equal; they each bring added value, with service objects focused on command functions. Though I don’t see a direct replacement, multiple patterns can help simplify workflows.
00:14:51.000
Your service objects could be further enhanced by orchestrating actions, potentially sharing context among them without complicating your code structure.
00:15:07.000
Think of orchestrated services as organized interactions that maintain clarity amidst complex relationships.
00:15:15.000
In summary, leveraging service objects thoughtfully can greatly enhance the architecture of your application. Thank you once again for your attention!