Slide 1

Slide 1 text

Composition & Domain Model Melnyk Volodymyr RubyC, Kyiv 2015

Slide 2

Slide 2 text

Who am I? It’s a hard question.

Slide 3

Slide 3 text

Rails Anti-Patterns

Slide 4

Slide 4 text

… also known as “Rails Way”

Slide 5

Slide 5 text

Single-Table Inheritance, Taxonomy and Platonism The way to create problems instead of relations

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

Gene / Meme / Trait That’s what matter

Slide 11

Slide 11 text

Why do we use STI? • We need STI when there is a necessity to work with a collection of entities of all subtypes, what is a pretty uncommon case itself and can be solved easily without STI. • We use STI because we know about Barbara Liskov’s Substitution Principle from SOLID, but we do not know about Composition approach. • We use STI because we don’t want additional relations to be created.

Slide 12

Slide 12 text

Problems resulted by STI • “type” is a system (metadata) column, but pretty often we use it on Domain Model Layer. • “type” is difficult to be changed. • We can’t add a constraint on “type”. • Probably, we will need to create other inherited classes. • Each subtype will need subtype-specific columns/attributes. So we will get all the problems bounded with NULL. • We can’t use subtype specific primary key and constraints on RDBMS layer. • Sometimes we can’t use a super type to get a collection of entities of all subtypes.

Slide 13

Slide 13 text

Example #1 class Comment < ActiveRecord::Base end class SuperComment < Comment has_many :not_so_super_comments end class NotSoSuperComment < Comment end Comment.joins(:not_so_supper_comments) ActiveRecord::ConfigurationError: Association named 'not_so_super_comments' was not found on Comment; perhaps you misspelled it?

Slide 14

Slide 14 text

Composition as a solution Use one “Base” relation and additional relations for specific attributes and use DAOs over “Base” and other relations to create single entity. Put business logic into this entity.

Slide 15

Slide 15 text

Hm… Looks like DataMapper

Slide 16

Slide 16 text

Composition/DDD approach Use DDD’s Entity pattern. Which is the same as mentioned before.

Slide 17

Slide 17 text

Polymorphic associations The second barrel to shoot both legs at the same time

Slide 18

Slide 18 text

Why do we use Polymorphic associations (PA)? • If an entity of some type can belong to many other entities of different types, but only to one of them at one time. • To avoid the creation of many very similar entities and relations.

Slide 19

Slide 19 text

Example #2 class Comment < ActiveRecord::Base belongs_to: :commetable, polymorphic: true end class Article < ActiveRecord::Base has_many :comments, as: :commentable end class Image < ActiveRecord::Base has_many :comments, as: commentable end

Slide 20

Slide 20 text

Problems resulted by PA • Commenting may require some additional logic which will be duplicated in many places (Article, Photo, etc.). Moving this logic into separate module isn’t very helpful. • We can’t use JOINs because we really have no commentable relation and we can’t row-by-row change relation which we join. • If we need some additional attributes/columns for commentable entities - we will have to add them to all commentable entities/relations. (counter cache is the simplest example). • You can’t define association in metadata and can’t add constraints on foreign keys. • You can’t use polymorphic associations in the case when, for example, Cargo has two addresses: cargos.destination_point, cargos.start_point.

Slide 21

Slide 21 text

Composition as a solution • We can create separate Commentable entity and “commentables” relation. • We can put all commenting- related attributes/columns and logic into this entity/relation. • We can create many different *Commentable entities and “*commentables” relations for logic and attributes/columns which depend on entity that we want to be commentable.

Slide 22

Slide 22 text

Composition/DDD approach • Follow the previous leads. • Use DDD’s Aggregate pattern to describe dependency between Comments and Commentable and also between Commentable and Article, Photo, etc.

Slide 23

Slide 23 text

ActiveRecord as a golden hummer "I suppose it is tempting, if the only tool you have is a hammer, to treat everything as if it were a nail.” - Abraham Maslow

Slide 24

Slide 24 text

Why do we use ActiveRecord • It’s good when we want to map a relation onto an entity. • It’s a pretty good variant of DAO for the simplest cases, like stupid simple CRUD operations. • ActiveRecord comes out of the box with Rails.

Slide 25

Slide 25 text

Problems resulted by ActiveRecord • Abstraction leaks on more complicated queries. Actually, it does even on not so complicated ones. • We can’t abstract from data storage with ActiveRecord. • It doesn’t allow to use many relations under the hood of entity. • AR’s models have too much responsibility.

Slide 26

Slide 26 text

Composition/DDD approach • We can use DataMapper pattern. • We can use the most primitive DataMapper implementation which uses predefined SQL queries.

Slide 27

Slide 27 text

Pseudo keys (Surrogate keys)

Slide 28

Slide 28 text

Why do we use pseudo keys • because it’s hard sometimes to find good natural key. • as composite primary keys require composite foreign keys and we think that they are difficult in usage. • because they simplify using of CoC principle.

Slide 29

Slide 29 text

Problems of pseudo-keys • In most cases we have no “id” in a domain model. • They doesn’t guarantee uniqueness of entity and we get doubles. • We need to make more JOINs. • Because of doubles it’s hard to make analytics. • It forces us to use DISTINCT. • We can’t use USING. • Pseudo-keys are platform dependent. It may be hard to move your DB from one RDBMS to another.

Slide 30

Slide 30 text

Example #3 # We have no Category with id == 5 a = Article.create title: "New Article", category_id: 5

Slide 31

Slide 31 text

Solution • Use natural keys if it’s possible. • Use composite foreign keys for composite primary keys. • Define associations with special expressions which automatically add constraints.

Slide 32

Slide 32 text

Example #4 class AddKeys < ActiveRecord::Migration def change execute <<-SQL ALTER TABLE categories DROP COLUMN id; ALTER TABLE categories ADD COLUMN category_name varchar(36) primary key; ALTER TABLE articles ADD COLUMN category_name varchar(36) references categories(category_name); SQL end end

Slide 33

Slide 33 text

Example #4 a = Article.create({ title: "My Article”, category_name: "unexistent category”}) PG::ForeignKeyViolation: ERROR: insert or update on table "articles" violates foreign key constraint “articles_category_name_fkey" DETAIL: Key (category_name)=(unexistent category) is not present in table "categories". c = Category.create category_name: "Programming" a = Article.create category_name: “Programming" SELECT * FROM articles JOIN categories USING(category_name);

Slide 34

Slide 34 text

Rails validation

Slide 35

Slide 35 text

We use validation: • on forms on front-end. • inside controllers (strong params, for example). • inside models.

Slide 36

Slide 36 text

Problems with Rails validation • Validation logic spreaded all around an app. • It’s hard to test because sometimes we need to create some associated records. • It’s not OO-way. It’s not compatible with the Single Responsibility Principle. • Sometimes we need additional logic to skip validation.

Slide 37

Slide 37 text

Composition/DDD approach • We can put all validation of user input into different Form- objects. • We can put code, which responds for consistency into Entity or Aggregate.

Slide 38

Slide 38 text

Custom Types/Domains

Slide 39

Slide 39 text

Problems with custom Types/Domains • Rails’s implementation of ActiveRecord pattern has no wrappers over original data to provide custom, more meaningful objects - ValueObjects, so we add a lot of logic into model. • It’s not the OO-way. It’s not compatible with the Single Responsibility Principle. • We have a lot duplications of code. • Our code is hard to test.

Slide 40

Slide 40 text

Composition/DDD approach • We can wrap values into ValueObjects to provide additional logic and limitations on them. • After that we can use ValueObjects as attributes of Entity.

Slide 41

Slide 41 text

Conclusion • Use Composition approach. • Use DDD. Develop your app on the top of Domain Model. • Use all features of SQL. • Learn relational theory. • Be careful about data consistency. • Don’t worry, be happy!

Slide 42

Slide 42 text

Thank you for your attention!

Slide 43

Slide 43 text

Any questions?