new features • Testability - how easy to set up all data and cover all cases, fake or substitute dependencies • Readability - how easy to navigate and supposes where a responsible code piece is placed
by a auth token 2. Validate address info 3. Calculate a total of all line items in a cart 4. Try to charge the corresponded amount from the user account 5. Prepare order params and save them into DB 6. Send an email to the user that order is created
background jobs, rake tasks • It’s hard to use intermediate pieces of logic in other parts of the app • Depending on real constants forces you to create if/else branches Imagine the situation when you need to send emails via other mailer or get/put data from other sources. Or validating data by other rules.
set up env to call because of binding with HTTP layer. It’s easy to do using rails helpers for testing. But anyway, to build a single operation your test env should set up HTTP stuff.
A single simple business operation takes roughly 70 lines of code • Secondary private methods • It forces you to create custom actions in your controllers
create if/else branches • Hard to reuse the entire operation in other parts of the application, for example, CLI, background jobs. • It’s hard to compose/chain with other pieces of an application. For example call controller methods in other places.
Using instance variables for keeping data Real constants/ Dependency Injection Thread-safe in Puma/Sidekiq Thread-safe by themselves Service.call(data) Mutable Yes Real constants No No Service.new(data).call/ Service.new.call(data) Mutable Yes Real constants Yes No Service.call(data) Immutable No Real constants Yes Yes Service.call(dependencies, data) Immutable No Dependency Injection Yes Yes Service.new(dependencies) .call(data) Immutable No Dependency Injection Yes Yes
are gateways of your application. They mustn’t contain business rules they can only preprocess your data to pass into your service object and postprocess result to create an appropriate response.
instance variables for keeping data Real constants/ Dependency Injection Thread-safe in Puma/Sidekiq Thread-safe by themselves Service.call(data) Mutable Yes Real constants No No
parts of the application, for example, CLI, background jobs. • It’s easy to compose/chain with other pieces of an application. Private methods can be separated and moved to another class. • Still depending on real constants forces you to create if/else branches
Using instance variables for keeping data Real constants/ Dependency Injection Thread-safe in Puma/Sidekiq Thread-safe by themselves Service.new(data).c all/ Service.new.call(da ta) Mutable Yes Real constants Yes No Service.new(data).call • Diffs • Service • Controller
parts of the application, for example, CLI, background jobs. • It’s easy to compose/chain with other pieces of an application. Private methods can be separated and moved to another class. • Still depending on real constants forces you to create if/else branches
instance variables for keeping data Real constants/ Dependency Injection Thread-safe in Puma/Sidekiq Thread-safe by themselves Service.call(data) Immutable No Real constants Yes Yes
parts of the application, for example, CLI, background jobs. • It’s easy to compose/chain with other pieces of an application. Private methods can be separated and moved to another class. • Still depending on real constants forces you to create if/else branches
instance variables for keeping data Real constants/ Dependency Injection Thread-safe in Puma/Sidekiq Thread-safe by themselves Service.call(depe ndencies, data) Immutable No Dependency Injection Yes Yes
parts of the application, for example, CLI, background jobs. • It’s easy to compose/chain with other pieces of an application. Private methods can be separated and moved to another class. • Now, we need to use another mailer, or fetch data from other source everything we need is to pass another object as a dependency.
better • Now, in each controller we need to setup dependencies. But in most cases your dependencies will be the same. • Using raising exception for controlling your business flow is not a good idea. Exceptions, they are for unexpected situation. It forces the Ruby to jmp on call call stack. It’s like a hack. It works. But it was intended for other purpose. • Private methods look ugly. They accept extra params.
better • Now, in each controller we need to setup dependencies. But in most cases your dependencies will be the same. • Using raising exception for controlling your business flow is not a good idea. Exceptions, they are for unexpected situation. It forces the Ruby to jmp on call call stack. It’s like a hack. It works. But it was intended for other purpose. • Private methods look ugly. They accept extra params.
better • Now, in each controller we need to setup dependencies. But in most cases your dependencies will be the same. • Using raising exception for controlling your business flow is not a good idea. Exceptions, they are for unexpected situation. It forces the Ruby to jmp on call call stack. It’s like a hack. It works. But it was intended for other purpose. • Private methods look ugly. They accept extra params.
Diffs • Service • Controller Usage Mutable/ Immutable Using instance variables for keeping data Real constants/ Dependency Injection Thread-safe in Puma/Sidekiq Thread-safe by themselves Service.new(depend encies).call(data) Immutable No Dependency Injection Yes Yes
better • Now, in each controller we need to setup dependencies. But in most cases your dependencies will be the same. • Using raising exception for controlling your business flow is not a good idea. Exceptions, they are for unexpected situation. It forces the Ruby to jmp on call call stack. It’s like a hack. It works. But it was intended for other purpose. • Private methods look ugly. They accept extra params.