rails_domain_model 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +6 -0
- data/README.md +235 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rails_domain_model.rb +13 -0
- data/lib/rails_domain_model/aggregate.rb +78 -0
- data/lib/rails_domain_model/command.rb +33 -0
- data/lib/rails_domain_model/command_handler.rb +30 -0
- data/lib/rails_domain_model/event.rb +2 -0
- data/lib/rails_domain_model/generators/aggregate/USAGE +8 -0
- data/lib/rails_domain_model/generators/aggregate/aggregate_generator.rb +25 -0
- data/lib/rails_domain_model/generators/aggregate/templates/aggregate.rb +2 -0
- data/lib/rails_domain_model/generators/aggregate/templates/domain_aggregate.rb +2 -0
- data/lib/rails_domain_model/generators/command/USAGE +8 -0
- data/lib/rails_domain_model/generators/command/command_generator.rb +25 -0
- data/lib/rails_domain_model/generators/command/templates/command.rb +2 -0
- data/lib/rails_domain_model/generators/command/templates/domain_command.rb +5 -0
- data/lib/rails_domain_model/generators/event/USAGE +8 -0
- data/lib/rails_domain_model/generators/event/event_generator.rb +26 -0
- data/lib/rails_domain_model/generators/event/templates/domain_event.rb +3 -0
- data/lib/rails_domain_model/generators/event/templates/event.rb +2 -0
- data/lib/rails_domain_model/version.rb +3 -0
- data/rails_domain_model.gemspec +26 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 23726a0f397c0857f1d1c0c45f0d2ceca03c8114
|
4
|
+
data.tar.gz: 0a84cc74f2bd937a67c6b832e0ef6ae876ea1c72
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7f9083c5a6093ab62f5a97c4fd13ed1017e68cbc436f6649107a95c60155bdc45a1b8ac65ccf0251d7926e68292f3f5bd90e97fb5ed1e0b5856046ea9096cd26
|
7
|
+
data.tar.gz: 0436d59646e94bdf6c05efef3787dd2a7df040dd1c3197e00120929e95d92e9f2177b2c64cf6db52bc13222c7b56a3586a95196e40d174a9535694c483b88e7b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at mail@anderslemke.dk. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
data/README.md
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
## Installation
|
2
|
+
|
3
|
+
Add this line to your application's Gemfile:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
gem 'rails_domain_model'
|
7
|
+
```
|
8
|
+
|
9
|
+
And then execute:
|
10
|
+
|
11
|
+
$ bundle
|
12
|
+
|
13
|
+
Or install it yourself as:
|
14
|
+
|
15
|
+
$ gem install rails_domain_model
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Your first dive into Domain-Driven Design, Event Sourcing and CQRS.
|
20
|
+
|
21
|
+
Let's say you're a software developer and that you've built your share of successful Rails apps. You're sitting together with a domain expert who wants you to build some software.
|
22
|
+
|
23
|
+
**YOU:** OK, what do you need?
|
24
|
+
|
25
|
+
**DOMAIN EXPERT:** I'm writing a lot, and I have these ideas that I want to share with the world. I've heard that the internet is a great place to do that.
|
26
|
+
|
27
|
+
**YOU:** I can help you with that.
|
28
|
+
|
29
|
+
````bash
|
30
|
+
$ rails new idea_sharing
|
31
|
+
$ cd idea_sharing
|
32
|
+
````
|
33
|
+
|
34
|
+
You really want to nail the domain model, so
|
35
|
+
|
36
|
+
````Gemfile
|
37
|
+
# Gemfile
|
38
|
+
|
39
|
+
gem 'rails_domain_model'
|
40
|
+
````
|
41
|
+
|
42
|
+
and, as usual,
|
43
|
+
|
44
|
+
````bash
|
45
|
+
$ bundle install
|
46
|
+
````
|
47
|
+
|
48
|
+
Before we can dive in with the domain expert, we have a little more plumbing to do.
|
49
|
+
|
50
|
+
````
|
51
|
+
$ spring stop # if you use spring
|
52
|
+
$ rails generate rails_event_store_active_record:migration
|
53
|
+
$ rake db:create db:migrate
|
54
|
+
````
|
55
|
+
|
56
|
+
(In case of problems with the above, please refer to the [RailsEventStore documentation](https://railseventstore.org/docs/install/)).
|
57
|
+
|
58
|
+
Now you're ready to dive in.
|
59
|
+
|
60
|
+
**YOU:** When you usually write, how do you approach that?
|
61
|
+
|
62
|
+
**DOMAIN EXPERT:** I sit at my desk and start writing a draft.
|
63
|
+
|
64
|
+
**YOU:** OK. Let's see...
|
65
|
+
|
66
|
+
You know, that in order for you to provide an interface that supports this situation, you will need some standard Rails infrastructure:
|
67
|
+
|
68
|
+
````bash
|
69
|
+
$ rails generate resource desk/drafts title:string body:text
|
70
|
+
$ rake db:migrate
|
71
|
+
````
|
72
|
+
|
73
|
+
Now let's create the `new` action:
|
74
|
+
|
75
|
+
````ruby
|
76
|
+
# app/controllers/desk/drafts_controller
|
77
|
+
|
78
|
+
class Desk::DraftsController < ApplicationController
|
79
|
+
def new
|
80
|
+
@draft = Desk::Draft.new
|
81
|
+
end
|
82
|
+
end
|
83
|
+
````
|
84
|
+
|
85
|
+
Now, in the view, let's put this:
|
86
|
+
|
87
|
+
````erb
|
88
|
+
<%# app/views/desk/drafts/new %>
|
89
|
+
|
90
|
+
<%= form_for @draft do |f| %>
|
91
|
+
<%= f.text_field :title %>
|
92
|
+
<%= f.text_area :body %>
|
93
|
+
<% end %>
|
94
|
+
````
|
95
|
+
|
96
|
+
Up until now, standard Rails stuff. You feel at home, right?
|
97
|
+
|
98
|
+
You can show this to your domain expert and get them writing.
|
99
|
+
|
100
|
+
**DOMAIN EXPERT:** Well, it's not pretty, but I can definitely get started on my draft here.
|
101
|
+
|
102
|
+
*****TIME PASSES*****
|
103
|
+
|
104
|
+
**YOU:** So, now you that you've worked on your draft. What do you want to do with it now?
|
105
|
+
|
106
|
+
**DOMAIN EXPERT:** Well, I'm not quite ready to show it to anyone yet, but it would be nice if I could store it somewhere. Then I could get back to it later and finish it.
|
107
|
+
|
108
|
+
**YOU:** Got it!
|
109
|
+
|
110
|
+
````
|
111
|
+
$ rails generate domain:command desk/store_draft
|
112
|
+
````
|
113
|
+
|
114
|
+
What? This is new! A command? What's that?
|
115
|
+
|
116
|
+
Well, a command lives in your domain and uses the ubiquitous language that you and your domain expert agrees upon.
|
117
|
+
|
118
|
+
Let's give them a button, and use the command in the controller.
|
119
|
+
|
120
|
+
````erb
|
121
|
+
<%# app/views/desk/drafts/new %>
|
122
|
+
|
123
|
+
<%= form_for @draft do |f| %>
|
124
|
+
<%= f.text_field :title %>
|
125
|
+
<%= f.text_area :body %>
|
126
|
+
<%= f.submit 'Store draft' %>
|
127
|
+
<% end %>
|
128
|
+
````
|
129
|
+
|
130
|
+
````ruby
|
131
|
+
# app/controllers/desk/drafts_controller
|
132
|
+
|
133
|
+
class Desk::DraftsController < ApplicationController
|
134
|
+
def create
|
135
|
+
Domain::Desk::Commands::StoreDraft.new(
|
136
|
+
draft_id: SecureRandom.uuid,
|
137
|
+
title: params[:desk_draft][:title],
|
138
|
+
body: params[:desk_draft][:body]
|
139
|
+
).execute!
|
140
|
+
end
|
141
|
+
end
|
142
|
+
````
|
143
|
+
|
144
|
+
OK. So now the controller basically just translates HTTP/REST to our domain language? Neat!
|
145
|
+
|
146
|
+
Let's look at that command. A command has several responsibilities:
|
147
|
+
|
148
|
+
- Define, in the ubiquitous language, how you interact with your domain model.
|
149
|
+
- Define which aggregate the command applies too, and how.
|
150
|
+
- Define which attributes are need to do what you want.
|
151
|
+
- Run validations on those attributes.
|
152
|
+
|
153
|
+
All that looks like this:
|
154
|
+
|
155
|
+
````ruby
|
156
|
+
# domain_model/desk/commands/store_draft.rb
|
157
|
+
|
158
|
+
class Domain::Desk::Commands::StoreDraft < DomainCommand
|
159
|
+
with_aggregate Domain::Desk::Draft, :draft_id, :store
|
160
|
+
|
161
|
+
attr_accessor :title, :body, :draft_id
|
162
|
+
|
163
|
+
validates :title, presence: true
|
164
|
+
end
|
165
|
+
````
|
166
|
+
|
167
|
+
But really, you can make it look, and work exactly how you'd like. Take a look in `domain_model/domain_command.rb` if you, for example, feel like using something particular for defining attributes and doing validations.
|
168
|
+
|
169
|
+
But, that `Domain::Desk::Draft` aggregate-thing, what is that?
|
170
|
+
|
171
|
+
````bash
|
172
|
+
$ rails generate domain:aggregate desk/draft
|
173
|
+
````
|
174
|
+
|
175
|
+
Take a look in `domain_model/domain/desk/draft.rb` which we will update to look like this:
|
176
|
+
|
177
|
+
````ruby
|
178
|
+
# domain_model/domain/desk/draft.rb
|
179
|
+
|
180
|
+
class Domain::Desk::Draft < DomainAggregate
|
181
|
+
|
182
|
+
def store(command)
|
183
|
+
_apply Domain::Desk::Events::DraftStored.new( data: {
|
184
|
+
draft_id: command.draft_id,
|
185
|
+
title: command.title,
|
186
|
+
body: command.body
|
187
|
+
} )
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def apply_draft_stored(event)
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
````
|
197
|
+
|
198
|
+
So, one last thing will be that event we just applied. Let's get that into place:
|
199
|
+
|
200
|
+
````bash
|
201
|
+
$ rails generate domain:event desk/draft_stored
|
202
|
+
````
|
203
|
+
|
204
|
+
That looks like this:
|
205
|
+
|
206
|
+
````ruby
|
207
|
+
# domain_model/domain/desk/events/draft_stored.rb
|
208
|
+
|
209
|
+
class Domain::Desk::Events::DraftStored < DomainEvent
|
210
|
+
end
|
211
|
+
````
|
212
|
+
|
213
|
+
And now, you're done! The draft is stored when you click the button!
|
214
|
+
|
215
|
+
Agreed, that was a bit more work than just saving the darn draft in the controller. What did you accomplish here?
|
216
|
+
|
217
|
+
Most importantly, you managed to wrangle yourself free from the vocabulary used by your technology stack. (Being HTTP/REST and an SQL database.)
|
218
|
+
|
219
|
+
You have established a vocabulary, containing the concepts `desk` , `draft` and `store`, that are completely controlled by your domain expert, and you have to perform zero translation when talking to them.
|
220
|
+
|
221
|
+
Also, you are now, officially, event sourcing. 🙌
|
222
|
+
|
223
|
+
## Development
|
224
|
+
|
225
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
226
|
+
|
227
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
228
|
+
|
229
|
+
## Contributing
|
230
|
+
|
231
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/anderslemke/rails_domain_model. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
232
|
+
|
233
|
+
## Code of Conduct
|
234
|
+
|
235
|
+
Everyone interacting in the RailsDomainModel project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/anderslemke/rails_domain_model/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rails_domain_model"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rails_event_store'
|
2
|
+
require "rails_domain_model/version"
|
3
|
+
|
4
|
+
module RailsDomainModel
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rails_domain_model/generators/command/command_generator'
|
8
|
+
require 'rails_domain_model/generators/aggregate/aggregate_generator'
|
9
|
+
require 'rails_domain_model/generators/event/event_generator'
|
10
|
+
require 'rails_domain_model/command_handler'
|
11
|
+
require 'rails_domain_model/command'
|
12
|
+
require 'rails_domain_model/aggregate'
|
13
|
+
require 'rails_domain_model/event'
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'aggregate_root'
|
2
|
+
|
3
|
+
AggregateRoot.configure do |config|
|
4
|
+
config.default_event_store = RailsEventStore::Client.new
|
5
|
+
end
|
6
|
+
|
7
|
+
class RailsDomainModel::Aggregate
|
8
|
+
|
9
|
+
def self.on(*event_klasses, &block)
|
10
|
+
event_klasses.each do |event_klass|
|
11
|
+
name = event_klass.name || raise(ArgumentError, "Anonymous class is missing name")
|
12
|
+
handler_name = "on_#{name}"
|
13
|
+
define_method(handler_name, &block)
|
14
|
+
@on_methods ||= {}
|
15
|
+
@on_methods[event_klass]=handler_name
|
16
|
+
private(handler_name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.on_methods
|
21
|
+
ancestors.
|
22
|
+
select{|k| k.instance_variables.include?(:@on_methods)}.
|
23
|
+
map{|k| k.instance_variable_get(:@on_methods) }.
|
24
|
+
inject({}, &:merge)
|
25
|
+
end
|
26
|
+
|
27
|
+
def _apply(*events)
|
28
|
+
events.each do |event|
|
29
|
+
_apply_strategy.(self, event)
|
30
|
+
_unpublished << event
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def _load(stream_name, event_store: _default_event_store)
|
35
|
+
@_loaded_from_stream_name = stream_name
|
36
|
+
_events_enumerator(event_store, stream_name).with_index do |event, index|
|
37
|
+
_apply(event)
|
38
|
+
@_version = index
|
39
|
+
end
|
40
|
+
@_unpublished_events = nil
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def _store(stream_name = _loaded_from_stream_name, event_store: _default_event_store)
|
45
|
+
event_store.publish(_unpublished, stream_name: stream_name, expected_version: version)
|
46
|
+
@_version += _unpublished_events.size
|
47
|
+
@_unpublished_events = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def _unpublished_events
|
51
|
+
_unpublished.each
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def _unpublished
|
57
|
+
@_unpublished_events ||= []
|
58
|
+
end
|
59
|
+
|
60
|
+
def version
|
61
|
+
@_version ||= -1
|
62
|
+
end
|
63
|
+
|
64
|
+
def _apply_strategy
|
65
|
+
AggregateRoot::DefaultApplyStrategy.new(on_methods: self.class.on_methods)
|
66
|
+
end
|
67
|
+
|
68
|
+
def _default_event_store
|
69
|
+
AggregateRoot.configuration.default_event_store
|
70
|
+
end
|
71
|
+
|
72
|
+
def _events_enumerator(event_store, stream_name)
|
73
|
+
event_store.read.in_batches.stream(stream_name).each
|
74
|
+
end
|
75
|
+
|
76
|
+
attr_reader :_loaded_from_stream_name
|
77
|
+
end
|
78
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class RailsDomainModel::Command
|
2
|
+
class ValidationError < RuntimeError
|
3
|
+
attr_accessor :errors
|
4
|
+
def initialize(errors)
|
5
|
+
@errors = errors
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_accessor :aggregate_class, :aggregate_id_attribute, :aggregate_method
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.with_aggregate(klass, id_attribute, method)
|
14
|
+
@aggregate_class = klass
|
15
|
+
@aggregate_id_attribute = id_attribute
|
16
|
+
@aggregate_method = method
|
17
|
+
|
18
|
+
validates @aggregate_id_attribute, presence: true
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute!
|
22
|
+
validate!
|
23
|
+
|
24
|
+
RailsDomainModel::CommandHandler.new(self).handle!
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def validate!
|
30
|
+
fail ValidationError.new(errors), errors.messages.inspect unless valid?
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class RailsDomainModel::CommandHandler
|
2
|
+
def initialize(command)
|
3
|
+
@command = command
|
4
|
+
end
|
5
|
+
|
6
|
+
def handle!
|
7
|
+
aggregate = aggregate_class.new._load(stream_name)
|
8
|
+
aggregate.send(aggregate_method, @command)
|
9
|
+
aggregate._store
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def stream_name
|
15
|
+
"#{aggregate_class}$#{aggregate_id}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def aggregate_class
|
19
|
+
@command.class.aggregate_class
|
20
|
+
end
|
21
|
+
|
22
|
+
def aggregate_id
|
23
|
+
@command.send(@command.class.aggregate_id_attribute)
|
24
|
+
end
|
25
|
+
|
26
|
+
def aggregate_method
|
27
|
+
@command.class.aggregate_method
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Domain
|
4
|
+
module Generators
|
5
|
+
class AggregateGenerator < Rails::Generators::NamedBase
|
6
|
+
source_root File.expand_path(File.join(File.dirname(__FILE__), './templates'))
|
7
|
+
|
8
|
+
def copy_files
|
9
|
+
@context = class_path.first
|
10
|
+
@klass = file_name
|
11
|
+
|
12
|
+
aggregate_file = "domain_model/domain/#{@context}/#{@klass}.rb"
|
13
|
+
|
14
|
+
template "aggregate.rb", aggregate_file
|
15
|
+
if !File.exists?('domain_model/domain_aggregate.rb')
|
16
|
+
template 'domain_aggregate.rb', 'domain_model/domain_aggregate.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
application do
|
20
|
+
"config.paths.add 'domain_model', eager_load: true"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Domain
|
4
|
+
module Generators
|
5
|
+
class CommandGenerator < Rails::Generators::NamedBase
|
6
|
+
source_root File.expand_path(File.join(File.dirname(__FILE__), './templates'))
|
7
|
+
|
8
|
+
def copy_files
|
9
|
+
@context = class_path.first
|
10
|
+
@klass = file_name
|
11
|
+
|
12
|
+
command_file = "domain_model/domain/#{@context}/commands/#{@klass}.rb"
|
13
|
+
template "command.rb", command_file
|
14
|
+
if !File.exists?('domain_model/domain_command.rb')
|
15
|
+
template 'domain_command.rb', 'domain_model/domain_command.rb'
|
16
|
+
end
|
17
|
+
|
18
|
+
application do
|
19
|
+
"config.paths.add 'domain_model', eager_load: true"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Domain
|
4
|
+
module Generators
|
5
|
+
class EventGenerator < Rails::Generators::NamedBase
|
6
|
+
source_root File.expand_path(File.join(File.dirname(__FILE__), './templates'))
|
7
|
+
|
8
|
+
def copy_files
|
9
|
+
@context = class_path.first
|
10
|
+
@klass = file_name
|
11
|
+
|
12
|
+
command_file = "domain_model/domain/#{@context}/events/#{@klass}.rb"
|
13
|
+
template "event.rb", command_file
|
14
|
+
|
15
|
+
if !File.exists?('domain_model/domain_event.rb')
|
16
|
+
template 'domain_event.rb', 'domain_model/domain_event.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
application do
|
20
|
+
"config.paths.add 'domain_model', eager_load: true"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "rails_domain_model/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rails_domain_model"
|
8
|
+
spec.version = RailsDomainModel::VERSION
|
9
|
+
spec.authors = ["Anders Lemke"]
|
10
|
+
spec.email = ["mail@anderslemke.dk"]
|
11
|
+
|
12
|
+
spec.summary = "An opinionated domain layer for Ruby on Rails."
|
13
|
+
spec.description = "Get control of your domain model with Event Sourcing, CQRS and core concepts from Domain-Driven Design. RailsDomainModel provides an opinionated domain layer for Rails."
|
14
|
+
spec.license = "LGPL-3.0"
|
15
|
+
spec.homepage = 'http://rubygems.org/gems/rails_domain_model'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
25
|
+
spec.add_dependency "rails_event_store", "~> 0.34"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_domain_model
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Anders Lemke
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-12-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.16'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.16'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rails_event_store
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.34'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.34'
|
69
|
+
description: Get control of your domain model with Event Sourcing, CQRS and core concepts
|
70
|
+
from Domain-Driven Design. RailsDomainModel provides an opinionated domain layer
|
71
|
+
for Rails.
|
72
|
+
email:
|
73
|
+
- mail@anderslemke.dk
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- ".rspec"
|
80
|
+
- ".travis.yml"
|
81
|
+
- CODE_OF_CONDUCT.md
|
82
|
+
- Gemfile
|
83
|
+
- LICENSE.txt
|
84
|
+
- README.md
|
85
|
+
- Rakefile
|
86
|
+
- bin/console
|
87
|
+
- bin/setup
|
88
|
+
- lib/rails_domain_model.rb
|
89
|
+
- lib/rails_domain_model/aggregate.rb
|
90
|
+
- lib/rails_domain_model/command.rb
|
91
|
+
- lib/rails_domain_model/command_handler.rb
|
92
|
+
- lib/rails_domain_model/event.rb
|
93
|
+
- lib/rails_domain_model/generators/aggregate/USAGE
|
94
|
+
- lib/rails_domain_model/generators/aggregate/aggregate_generator.rb
|
95
|
+
- lib/rails_domain_model/generators/aggregate/templates/aggregate.rb
|
96
|
+
- lib/rails_domain_model/generators/aggregate/templates/domain_aggregate.rb
|
97
|
+
- lib/rails_domain_model/generators/command/USAGE
|
98
|
+
- lib/rails_domain_model/generators/command/command_generator.rb
|
99
|
+
- lib/rails_domain_model/generators/command/templates/command.rb
|
100
|
+
- lib/rails_domain_model/generators/command/templates/domain_command.rb
|
101
|
+
- lib/rails_domain_model/generators/event/USAGE
|
102
|
+
- lib/rails_domain_model/generators/event/event_generator.rb
|
103
|
+
- lib/rails_domain_model/generators/event/templates/domain_event.rb
|
104
|
+
- lib/rails_domain_model/generators/event/templates/event.rb
|
105
|
+
- lib/rails_domain_model/version.rb
|
106
|
+
- rails_domain_model.gemspec
|
107
|
+
homepage: http://rubygems.org/gems/rails_domain_model
|
108
|
+
licenses:
|
109
|
+
- LGPL-3.0
|
110
|
+
metadata: {}
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 2.6.8
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: An opinionated domain layer for Ruby on Rails.
|
131
|
+
test_files: []
|