Camelia, the Perl 6 bug

IRC log for #cqrs-perl6, 2012-09-21

| Channels | #cqrs-perl6 index | Today | | Search | Google Search | Plain-Text | summary

All times shown according to UTC.

Time Nick Message
08:28 dams good morning
08:29 dams I have an anxious question
08:30 dams 22:27 < dams> ok. But it's still ok for a talk to have an id pointing to a speaker ?
08:30 dams 22:29 < masak> dams: yeah, but the "pointer" here is just a string UUID or something.
08:30 dams that feels wrong somewhere. So I must be missing something here
08:31 dams it feels wrong to me because if there is a conceptual relationship between a Speaker and a MongerGroup for instance.
08:31 dams in standard DB, you'd have a monger_group_id in the Speaker table, to indicate that the Speaker belongs to this MongerGroup
08:32 dams and then if you have a function to remove a MongerGroup, it'll also remove the relationship with all its speaker, thus set the monger_group_id to null, or something
08:33 dams in your design, you seem to say that the Speaker Object should refer to the MongerGroup it belongs to, only by using its UUID
08:33 dams but a UUID is not something you can do an DB index on (as far as I know).
08:34 dams and so you won't be able to use foreign keys features etc. And so, when deleting a MongerGroup, cleaning up the Speakers that belongs to it will be painful and very slow
08:34 dams so, what am I missing here ?
08:45 BooK I'd say: the fact that the db content is generated from the events
08:45 BooK so objects have a uuid, and there representation in the db have the usual ids and relations
08:45 BooK getting an object if a read, which you do from the storage
08:46 BooK updating an object is a write, for which you need the uuid
08:46 BooK in my understanding, the aggregates interact with the storage when applying the events
08:48 masak dams: congratulations. you have reached the "foreign key withdrawal stage" ;)
08:49 masak this happens to everybody after hearing about aggregates.
08:50 BooK masak: time to gamify cqrs
08:50 masak let me first restate dams' problem so that I understand it fully.
08:51 masak there is a MongerGroup aggregate type, and a Speaker aggregate type. a Speaker aggregate can refer to a MongerGroup UUID.
08:51 masak so far so good.
08:52 masak dams, what you are anxious about is a kind of consistency -- the fact that the MongerGroup should exist if a Speaker refers to it.
08:52 masak did I get that right?
08:55 BooK and obtain one from the other
08:56 masak well, read sides can do all the combinating for us.
08:56 BooK given a group, get the list of people in it, given a person, get the list of groups they belong to
08:56 masak right. that's reporting territory. no big deal.
08:56 BooK but the aggreagtes are made of objects which are fleshed by the read side, right?
08:57 masak typically what a read side gets is a stream of events.
08:57 masak which it can then build views from.
08:58 masak so a read side which gets both MongerGroup and Speaker events can easily build lists of groups and people just like you envisioni.
08:58 masak envision*
08:59 BooK so we have storage, which the read side uses to build the views, and that the write side updates
08:59 dams sorry guys, have to be afk ($dayjob ...)
08:59 dams I'll be back asasp
08:59 masak no prob.
09:00 masak BooK: right. if the storage is an event store, then it's the same stuff being stored as being sent across to the views on the read side.
09:01 masak if it's a 3NF SQL database, then you have to project the events down to it as well.
09:01 masak (and then you can't re-run the history of events)
09:01 BooK you can have both
09:01 masak aye.
09:02 BooK archive the events in an event store, and update the db
09:02 masak but I wouldn't :)
09:02 masak anyway, my reply to dams is two counter-questions, namely (a) what is the user benefit you're trying to produce by maintaining this consistency, and how immediate must it be? and (b) are you sure "remove" means "eradicate from the database"? that seems a bit drastic.
09:03 BooK I think dams was thinking about, say, showing a user profile web page
09:03 BooK that lists the groups they belong to
09:03 masak nod.
09:03 masak seems to me you might be conflating User and Speaker so far, but nevermind.
09:03 BooK just an example
09:03 masak maybe you even want that.
09:03 BooK (roles)
09:03 masak right.
09:04 masak well, a user profile web page is a read side. so we're back to being able to compose anything we want there.
09:04 BooK we try to thing in terms of the smallest nugget of identity there is, and then add to it
09:04 masak nod.
09:04 BooK like a D&D chracter starting naked, and getting pants, shoes, swords, helmet etc
09:04 masak :)
09:05 BooK so the most basic thing would be a Person
09:05 masak first time I hear speaking at a conf being compared to getting pants :P
09:05 BooK basically a name
09:06 BooK actually, we could even go further (but I disgress), with the identity being the UUID, and get it a collection of attributes, like name, email and stuff
09:06 BooK so, lets assume we have Person, has => name, and Group , has => name
09:07 masak nod.
09:07 BooK how do we model the relationships of Groups and People ?
09:07 BooK in the objects, and further down to storage
09:07 masak there's no one answer to "how do we model X?"
09:08 masak depends on what your use cases are. depends how you plan to use the information.
09:08 BooK well, we could say that for a given event, we want to send invitations to all persons in a group
09:09 masak right, so you're after the side effect of sending email.
09:09 masak think about the case of putting together the "To:" field in that email. that's making a request about an aggregation of stuff.
09:10 masak so you go to a view on the read side. maybe the view presents you with the whole invitation email, I dunno.
09:10 masak but it has enough projected information to give you the right "To:" field.
09:10 masak so you put that email in your email client and press "Send".
09:11 masak or you have a button in your admin interface which connects to an email function which does it for you.
09:11 masak see what I mean? the point is that just because you isolate things well in aggregates, you're not limited to making UI actions on single things. you can get arbitrarily fancy in the UI.
09:12 BooK not really
09:12 masak it's like REST resources -- they don't have to be limited to single model entities, and thinking of them that way seriously limits one's options when modeling.
09:12 BooK I was after a list of users that I can use as I see fit: send email, list on a web page, save to a file
09:12 masak right.
09:13 masak those sound to me like different but related views on the read side.
09:13 masak collecting the events coming out of the write side to combine information from Group and Person aggregates.
09:13 BooK like, one view with the list of properly formatted email addresses, vs a list of name with links to profile pages?
09:14 masak yeah.
09:14 BooK we don't have objects anymore, right? objects are also a projection of the events?
09:15 BooK if we don't care about the Person's pants, the object we get from the view doesn't need them
09:15 BooK so won't have them
09:16 BooK sounds like we're back to 1. steal pants, 2. ..., 3. profit! :-)
09:17 masak :P
09:18 masak on the write site and with an event store, the aggregates are also rebuilt from the history of events, yes.
09:18 masak and on the read side, if we don't care about some bit of information, we don't project it, correct.
09:19 BooK isn't that awfully slow?
09:21 masak which bit? rebuilding an aggregate from a sequence of events?
09:22 BooK the read side works with the projections, right?
09:22 masak I don't understand the quesion.
09:23 masak please rephrase.
09:23 BooK is the classical set of tables with a person table, a group table and a link table for the relationship between the two a projection ?
09:24 masak yeah, could be. but it's a fairly normalized one.
09:24 masak projecting down to a read side often means doing a bit of denormalization on the way, like pre-chewing food.
09:25 masak so you might be better off (depending on what you want to read out later) with a big table with one column for person and one for group, and each row being a pairing.
09:27 BooK how is the persistence of aggregates achieved?
09:28 BooK seems the event store is a way, but that means rebuilding the aggregates at startup, from the beginning of times
09:28 BooK so there has to be a way to "save" the current set of aggregates
09:29 masak if you're using an event store, you rebuild the aggregate each time a command comes in.
09:29 masak it's much cheaper than you think.
09:30 masak there are techniques for memoizing that cost, but talking about them now would distract you from how fast it is just to rebuild stuff each time.
09:31 BooK I think we really need to write some code and try this out
09:32 masak aye.
09:35 BooK https://github.com/book/cqrs-example # with dams, masak and jnthn as collaborators
09:35 masak :)
09:36 masak I need to do a bit of $dayjob now.
09:36 BooK mmm, me too
09:36 BooK I don't know what you want to do with cqrs, but if you're looking at evangelizing, we could come to a win-win situation :-)
09:37 masak we've been into CQRS for two years now. the fascination with it doesn't seem to subside. rather the reverse -- we're coming up with better and better ways to make use of it.
09:39 dams back.
09:41 dams masak: so yes, you got it right, anxious about the consistency, but also not sure how to read the associated data efficiently
09:43 dams ( backlogging )
09:48 dams thanks masak for the answers. I think we need an event storage. I doubt using a RDBM as storage would do it. It would be awfully slow
09:51 masak not sure it would, but there are event stores out there which might appeal to you.
09:52 masak Greg Young recently released http://geteventstore.com/
09:57 dams one last question to see if I understood correctly : if there's a command : "unlink this User to all the conference he is attached to". Would the events be :
09:58 dams - grab the User aggregate, grab the User/Conference relation Aggregate, resolve on User, then grab the Conferences, and for each of them, unlink the User ?
09:58 dams ( ok I got the relationship the wrong way but you see what I mean)
09:58 dams or would it be muh simpler ?
10:00 dams hm. I guess I need to do more doc reading and code reading.
10:03 masak conceptually, a command acts on one aggregate only. you don't grab any more aggregates than the one you're acting on.
10:04 masak there are use cases when you'll want to work on more than one aggregate, but they will have to be split up on several commands. there are patterns for this.
10:05 masak if the User aggregate owns the "which conferences" data, the above operation is trivial. whether it should depends on the surrounding context.
11:43 BooK the docs/ section of the above web site helps understanding the conceots
11:43 BooK -cepts
11:46 masak oh, excellent. I haven't read that section yet; the site is quite new.
11:46 masak but yeah -- my understanding of these concepts comes largely from Greg Young's courses and online docs.
11:55 BooK I noticed this: "It needs to be made clear at the very start of this section that the value of the Event Log is directly correlated with places that you would want to use Domain Driven Design in the first place. Domain Driven Design should be used in places where the business derives competitive advantage. Domain Driven Design itself is very difficult and expensive to apply; a company will however receive high ROI on the effort if the domain is complex an
12:04 masak as far as the quote was pasted, I agree fully.
12:05 masak Domain Driven Design is wonderful but takes real time and effort.
12:05 masak do not, I repeat, do not apply it to your application Settings pane.
12:05 masak (because that one probably is and should be CRUD)
12:06 jnthn And probably should be an XML blob rather than relational too :P
12:06 * jnthn mumbles about $today-client's 100ish column, one-row settings table
12:08 jnthn .oO( typical example of the pain of relational dominance... )
12:16 BooK would it make more sens to start in the "traditional" way (Moose + DBIC + some web framework) and then decide if for some subset we should apply DDD ?
12:18 jnthn DDD is really good for the core domain - the thing that's at the heart of what you're building
12:19 jnthn So much hinges on if you have a good feel for what your core domain is
12:24 BooK the domain is "gatherings": specific people in a specific place at a specific time
12:32 jnthn OK, so then it seems you have an idea of the core domain, so I'd start there. Of course, there'll be details (like, info about people, info about places) but that info may not be "core" in a sense - as in, that's not where the real value in what you're building is.
12:34 BooK should we start by the events?
12:35 BooK like GatheringCreated, LocationAdded, DateDecided, PersonRegistered, etc?
12:37 jnthn Yes, that tends to work out fairly well
12:37 jnthn Then look at the invariants you'll need to enforce over time and that can help lead to the aggregates
12:41 masak it's a little difficult at first not to skip ahead and assume that you know what your aggregates are.
12:43 masak that's what the "focus on the verbs" slogan aims to capture. the events are primary; the aggregates are just convenient data containers to help uphold invariants.
12:44 jnthn Yeah. I found aggregate hunting harder before I realized they're really centered around invariants.
12:48 masak interesting how that, too, comes back to SRP.
12:49 masak I also have a hunch that focusing on the lifetimes and life cycles involved in the application is very revealing for finding aggregate boundaries.
12:49 masak put things together that vary together. or that get created/deleted together.
12:58 BooK SRP?
12:58 BooK I wonder what would be our invariants there
12:59 masak Single Responsibility Principle
13:00 masak I bet you use it when designing classes, even if you don't recognize the name.
13:36 BooK I think right now we're sitting on the fence. DDD and CQRS look interesting and powerful, but why should we invest time and effort to use this in our project, instead of the things we know already how to use?
13:36 masak right.
13:36 masak maybe try it with something smaller first, like we did with the hex project?
13:36 masak even that, of course, is assuming your spare time is infinite... :)
13:37 BooK hehehe
13:37 BooK that why I asked 14:16 < BooK> would it make more sens to start in the "traditional" way (Moose + DBIC + some web framework) and then decide if for some subset we should apply DDD ?
13:38 BooK because starting in the usual way would be motivating (getting something working done quickly), useful (getting better knowledge of the domain)
13:38 BooK so starting to experiment with DDD/CQRS might be easier
13:39 masak I'm all for releasing early and getting things working quickly.
13:40 masak personally I think the modeling is worth doing with DDD in mind regardless of whether you proceed with DDD afterwards.
13:40 masak good for evolving a ubiquitous language and finding out how your concepts interact and what verbs you care about.
13:49 BooK yes, the ubiquitous language sounds like a very good thing to have
13:49 BooK I found the DDD book on Amazon, should I order it?
13:51 BooK I mean, have you guys read it, and if so do you recommend it?
13:52 masak I own the book. I heartily recommend it.
13:52 masak I'm reading it in my copious spare time.
13:53 masak it's delightfully informative. in some ways it talks about things that I "know already" but have never been able to express myself or seen in print.
23:11 BooK I didn't realize it was a 500 pages book :-)

| Channels | #cqrs-perl6 index | Today | | Search | Google Search | Plain-Text | summary