TIL, 2018-03-12, History Lesson
Musings
- Reviewing through “Growing Rails Applications in Practice.” I still completely agree on controllers and models. AR models should really be as slim as possible.
- Checked out some use cases for NoSQL. I think it’s for bigger applications. At the start, you stick with pure RDBMSes.
-
Elixir’s
defmacro __using__
. - When to index?
- You can add a double index when you need to do something like
SELECT name from repositories where owner_id > 500
. Create an index overowner_id
andname
. - Common rules of indexing, querying, and data modeling.
- Any columns involved in queries should be indexed, unless there is already a covering index.
- Problems with redundant index: it takes up precious space, and adding an index slows down updates/inserts/deletes because those operations also have to edit the index.
- Index prefix vs index.
- If you’re indexing over a long data type, you can use this.
- If it’s things like “query usernames starting with ‘a’”.
- How long? Long enough to differentiate values within the field, and base it off of real data if possible.
- Use an
OR(UNION) to satisfy.EXPLAIN
shows what indexes were use and how many rows were scanned.- The
OR
operator is limited because MySQL can only use one index per table during a query, so it chooses to use neither. - You can use a UNION if there’s no index that covers both conditionals at the same time.
- If there’s an index, you’re all set.
- You can use a
FORCE INDEX
orUSE INDEX
orIGNORE INDEX
if MySQL is not choosing a performant index.
- You can use a
- Avoid redundant data across tables.
- Unless additional reads/JOINS are causing a large amount of performance overhead.
- Unless if there is a high ratio of reads to writes.
- Cost: Database changes (migrations) and data quality (since no longer normalized).
- Any columns involved in queries should be indexed, unless there is already a covering index.
- You can add a double index when you need to do something like
- I just cleaned through Yehuda Katz talk about “The Next 5 Years of Rails” in 2012, that was freaky! It really happened.
- Mistakes with the Repository Pattern
- One repository per domain: separate per domain class:
OrderRepository
,ShippingRepository
,ProductRepository
. - Returning view models: Your repositories should return domain objects and the client of your repository can decide if it needs to do the mapping.
- Saving/updating method in repositories: Do not have a
save()
orupdate()
method. Think of a repository as a collection of domain objects in memory. Collections do not have a save orupdate()
method. The pattern that comes with this is theUnit of Work
pattern. After doing the operation, save it with another class. - repositories that return
IQueryable
. You should return domain objects. - The more complex your application grows, the more you may want to consider separating the read and write models, and then eventually, you’ll find in CQRS.
- One repository per domain: separate per domain class:
- Q&A with Piotr Solnica.
- Dry: Using both OO and FP features. Things to avoid in the Ruby ecosystem: monkey-patching, relying on object mutability. Promote: DI, object composition, type safety.
- DI is easier in Ruby than in a statically-typed language like Scala.
- Some people just have a certain preference for the DSL they use. ActiveModel vs dry-validation. Control flow in monads?
- Making Ruby code more functional has made it simpler and faster. Crucial parts of your system, like data validation/type conversions/error handling.
- Composition: when you use callable objects that don’t rely on state mutations, it’s much easier to compose them. You can change your approach to OO/FP. Two key factors that help me improve the design were moving away from relying on mutable state, and isolating the complexity of object construction via inversion of control containers.
- Downside of
dry-rb
: they are small, simple abstractions, and people who are used to more complex, well-integrated frameworks will simply have a steeper learning curve. - No need to change objects, and you can visualize an application as a series of data transformations rather than object interactions, where data is part of state in order to “encapsulate” it.
- Instead of: creating an AR instance then saving, you can validate the params, then ask a Repository object to persist valid data.
dry-struct
, thendry-validation
.
- Glanced through some TDD things.
- Hexagonal Rails and The Ludicrous Terminal Application.
- Very rare to do “swappable data stores and UIs” (though in Daryllxd that’s what’s happening right now, lol).
- More likely, you need to separate domain from persistence since they change for different reasons.
- Whenever Rails updates, if you put int business logic in controllers and Ar models, you run the risk of having to change the objects containing the application’s business logic.
- When you have to change business logic in order to speed of persisting and retrieving data from the data store. When you need to change persistence strategies to accommodate tables, stored procedures, sharding, if you combined business/persistence logic, your business logic will have to change with it.
- When business logic gets entangled with the framework, velocity slows, defect rates increase, and developer happiness drops.
- Other consumers of the app: Scheduled tasks, jobs, JSON API.
- Hexagonal Architecture for Rails Developers
- Modules can be developed and deployed independently.
- The application becomes easy to test because the logic that needs to be tested depends neither on the UI of the application nor the database.
- Techniques
- Case services: A component taht is responsible for the coordination logic we tend to put into our controllers.
- Passive controller: No decisions whether the use case it invoke succeeded or failed. It just returns whatever to the outside world.
- Adapters:
OrderController
andOrderRepository
. - The application knows nothing about persistence. It uses repositories to talk to the database.
- Hexagonal Rails and The Ludicrous Terminal Application.
Full Stack React
- If you do this (from Babel’s
transform-class-properties
plugin), thethis
inside the function is bound to the component, as expected.
handleUp = () => (
this.props.onVote(this.props.id, "pants")
);
- I can do
console.log(this.state)
. - Property initializers: We can use arrow functions for custom component methods (and avoid having to bind
this
). - We can define the initial state outside of the
constructor()
.