Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow admin to remove VAT rate #413

Merged
merged 3 commits into from
Oct 18, 2024
Merged

Allow admin to remove VAT rate #413

merged 3 commits into from
Oct 18, 2024

Conversation

marlena-b
Copy link
Collaborator

Issue: #408

This PR allows admin to delete VAT Rate.

Nagranie.z.ekranu.2024-10-9.o.14.09.11.mov

Copy link

netlify bot commented Oct 9, 2024

Deploy Preview for ecommerce-events failed.

Name Link
🔨 Latest commit 795e676
🔍 Latest deploy log https://app.netlify.com/sites/ecommerce-events/deploys/6710be4bb7e2c80008ab5241

Comment on lines +26 to +29
<%= form_with url: available_vat_rates_path, method: :delete do |f| %>
<%= f.hidden_field :vat_rate_code, value: available_vat_rate.code %>
<%= f.submit 'Delete' %>
<% end %>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used form_with instead of link_to because I needed to send vat_rate_code, which is our identifier. The vat_rate_code can contain a dot, e.g., 10.0. Using link_to the code was added as a parameter to the url, which caused issues with Turbo. It appears that Turbo does not handle URLs with dots in the path correctly (see hotwired/turbo#608).

To avoid this issue, I decided to send the parameter through the form instead.

@marlena-b marlena-b marked this pull request as ready for review October 9, 2024 12:46
&.fetch(:vat_rate)
&.then { |vat_rate| Infra::Types::VatRate.new(vat_rate) }

if last_available_vat_rate_event.instance_of?(AvailableVatRateAdded)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you only care about the AvailableVatRateAdded event then you could use RES client's api to get that:
@event_store.read.stream("Taxes::AvailableVatRate$#{vat_rate_code}").of_type(AvailableVatRateAdded).last

the if statement wouldn't be necessary

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually care that the last event is AvailableVatRateAdded and not AvailableVatRateRemoved to ensure that this vat rate wasn’t removed.

@@ -16,13 +16,17 @@ def vat_rate_for(product_id)
end

def vat_rate_by_code(vat_rate_code)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outside of scope of this task, but this method got me thinking.

This method is called in the AddAvailableVatRateHandler

It is called to check if tax rate with given code already exists. If not, then the handler publishes an event.

First of all, it breaks the tell don't ask rule. In current implementation there is possibility that two AvailableVatRateAdded events will be published. Therefore, the system will be in unexpected state.

The question is if that is a major or minor problem.

Since this if statement has been added for some reason, let's assume it would a major issue if the vat rate code is duplicated.

To reproduce this behaviour you can follow those steps:

rails-application(dev)* fiber = Fiber.new do
rails-application(dev)*   unless event_store.read.stream("AvailableVatRate$#{vat_rate_code}").last
rails-application(dev)*     Fiber.yield
rails-application(dev)*     event_store.publish(AvailableVatRateAdded.new(data: { vat_rate_code: }), stream_name: "AvailableVatRate$#{vat_rate_code}")
rails-application(dev)*   end
rails-application(dev)> end
rails-application(dev)> fiber.resume
  RubyEventStore::ActiveRecord::EventInStream Load (0.6ms)  SELECT "event_store_events_in_streams".* FROM "event_store_events_in_streams" WHERE "event_store_events_in_streams"."stream" = $1 ORDER BY "event_store_events_in_streams"."id" DESC LIMIT $2  [["stream", "AvailableVatRate$60"], ["LIMIT", 1]]
=> nil # <========= The if statement was checked here, there was no event in stream
rails-application(dev)> event_store.publish(AvailableVatRateAdded.new(data: { vat_rate_code: }), stream_name: "AvailableVatRate$#{vat_rate_code}")
rails-application(dev)> event_store.read.stream("AvailableVatRate$#{vat_rate_code}").to_a.count
=> 2

How do you think we could deal with that? (tip: look at the core concepts section in RES documentation)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the implementation of command handlers not to use vat_rate_catalog. Instead, I'm fetching the last event and using the expected_version mechanism to ensure there is no race condition. Is this what was concerning you?

I think my code still breaks tell don't ask principle (bc I'm asking what was the last event version) but I'm not sure how can I do it otherwise.

Thank you for spotting this and please let me know what you think.

Copy link
Collaborator

@lukaszreszke lukaszreszke Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what was concerning you?

Yes, exactly. Good job :)

I think that code is in ok shape now. I wouldn't optimize it anymore at this stage.

There are no business rules or invariants that would justify introducing an aggregate here. I would leave it as it is and observe how this code changes over time.

If, say there is another place that needs the piece of code that: reads the vat rate stream, checks if last vat rate event is X and does something, then I'd think about introducing some concept (not necessarily an aggregate) to keep it together. Perhaps Domain Service?

Comment on lines +14 to +15
- Taxes::RemoveAvailableVatRateHandler*
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I excluded them from mutant because I think the amount of weird tests I would have to write does not justify the benefit we get.

.stream("Taxes::AvailableVatRate$#{vat_rate_code}")
.last

event if event.instance_of?(AvailableVatRateAdded)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the method name does something different than the name declares.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored it a bit. I hope it is better now.

@marlena-b marlena-b merged commit 20c9c50 into master Oct 18, 2024
27 of 31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants