UIs:
- CRUD (these suck)
- task-based (users like those)
Aggregate:
- group of object we treat together as a whole
- affect only a single aggregate - that lets you avoid distributed transactions (think horizontal partitioning/sharding)
- put the method next to the state it operates on is
- denormalization helps to get the design right
Booksto read:
- Streamlined object modelling
- time interval object
- make implicit explicit
- Object-oriented software construction 2nd edition by Bertrand Meyer
- describes CQS
- business doesn't care about consistency
- breaking bidirectional relationships
- ask: do those things need to be consistent?
- drop consistency of invariant
- domain model != data model
- if needed, a Domain Service can ensure consistency (this should really be used only as a last resort!)
- collection of Transaction objects can have a domain meaning
- AggregateRoot (AR) name makes sense for the entire aggregate
- too much magic is bad (think ORM)
- between aggregates use soft links (IDs) instead of references
EXERCISE: test-drive Probability value object class with methods like combine(Probability), not() etc, encapsulating a Java's BigDecimal (.NET's Decimal?). The tricky part: you can't have any kind of accessor methods to expose the internal state. What do you test first?
And now... suppose that standard BigDecimal implementation is too slow for your system. You have to change the implementation of the Probability class but retain the API. How many tests do you have to change?
personal note: this turned out to be an easy, yet an interesting exercise. Funny, how it changes the way you write code when you don't have those evil getters around. I really, really liked it!
Repository:
- Evans: works on aggregates, provides domain language to persistence infrastructure
- Fowler: purely technical stuff
- make contracts in the domain:
- as narrow as possible
- as explicit as possible
- this will lower the conceptual coupling
- any piece of procedural code
- can be:
- infrastructure
- domain
- application
- but it the end they are all facades
- if you do things right you might never need services
- interface segregation
- single method interfaces
- role interfaces
Hexagonal architecture - ports & adapters
TIP: why not check check-ins for illegal dependencies (like domain depending on something else) and reject those that don't follow the rules?
On SOLID principles:
- they are just heuristics
- don't try to stick to them no matter the cost (duplication sometimes can be a good thing!)
EXAMPLE: When not to adhere to Interface Segregation?
- when all methods go the the same source
// client code:
void DoesSomething(ICanSeek seeker, ICanRead reader) {
seeker.seekTo(0);
void DoesSomething(ICanSeek seeker, ICanRead reader) {
seeker.seekTo(0);
while (var x = reader.read() != null) {
/...
}
}
ICanSeekRead extends ICanSeek,ICanRead != Foo implements ICanSeek, ICanRead
- DI/IoC:
- ServiceLocator is totally OK when resolving things at the same layer
- about injecting into entities: most dependencies match the lifecycle of methods, not objects
void Submit(ISearchDriverLicences s) {
s.searchFor("something");
}
- in functional programming DI can be implemented with function currying
void F() {//coupling from F to G
G.Something();
}
interface ISomething{
something();
}
void F(ISomething s) {
s.something();
}
class ISomethingImpl:ISomething {
// sometimes DI is too much:
void something(){
Console.WriteLine("Hello world");
}
}
- we're overusing tools, frameworks
frameworks pollute our brains
Back to services:
- ApplicationServices
- should be role interfaces, one for every use case of the system
- you should have no business logic in them (not even an if statement!)
isValid() antipattern
- pure evil!
- don't do that!
- causes GIGO (Garbage In, Garbage Out)
- entities end up being in one of 3 possible states:
- valid
- invalid
- have no frakking clue
- encapsulation is about protecting state - don't let people jam it!
public class AService {
AService( IEnumerable<Specification<Customer>>
rules){}
void deactivate(Customer c) {
if(!rules.areAllValid(c) {
throw new IllegalArgException();
}
...
}
}
=== END OF DAY 1 ===
Unfortunately, those notes don't show how absolutely awesome the training was. Really got my eyes wide open on many issues that I was somehow missing before.
On a related note - people really do use functional programming in real-world applications! After hearing that from Greg I started learning Clojure. I had a Prolog & Haskell course back at the university. I didn't like the Prolog part but really enjoyed writing minimized code in Haskell. Now I just have to find some time to refresh by skills at functional programming. Or better - find some use for it so I can justify re-learning it at work ;)
Brak komentarzy:
Prześlij komentarz