mendicant-original / mentoring

9 stars 1 forks source link

Mentoring session request #44

Closed elvanja closed 12 years ago

elvanja commented 12 years ago

Hi, I'd like to participate in a mentoring session regarding improving developing skills in Ruby.

Particularly, I am interested in a Game Of Life problem I'm working on for our local Coding Dojo. Sources can be found here. The issue is that I'd like it to be a fully OO solution and to serve as a base for a distributed version (spanning multiple computers).

If that problem is beyond the scope of such a mentoring session, I'd also be grateful for any input regarding the puzzles from puzzlenode for my MU application. Sources available here.

Thanks in advance, Vanja

Me

Vanja Radovanović elvanja @ gmail.com living and working in Pula, Croatia, GMT+1 available all the time during the weekends or after 6pm on workday except Fridays which are entirely occupied also unavailable 25th and 28th of February

ericgj commented 12 years ago

Hi Vanja, how about Tuesday Jan 17 at 6pm your time? If it's ok with you, could we focus first on the puzzlenode solutions then look at the Game of Life? Eric

elvanja commented 12 years ago

Hi Eric, Tuesday 6pm is great, thanks! And yes, we can do it in any order you feel more comfortable with (after all, I'm the one asking for help :-)) Vanja

ericgj commented 12 years ago

Hi Vanja, I just realized I didn't say where we were meeting. Maybe you figured it out, but I'm on #mendicant-study-hall-1 on freenode IRC. Actually I need a few minutes before we start if that's ok...

elvanja commented 12 years ago

Hi! Sure, no problem. I hope I connected correctly so if you can't see me try me on email...

2012/1/17 Eric Gjertsen < reply@reply.github.com

mendicant-study-hall-1

ericgj commented 12 years ago

Hi Vanja, good talking with you the other day. Here's the transcript and etherpad of our session. To help improve the mentoring sessions, I'd really appreciate if you could fill out this survey when you get a chance.

elvanja commented 12 years ago

Hi Eric, I already filled this form, as per instructions @ https://github.com/mendicant-university/mentoring/wiki/Instructions-for-students,

do you need me to repeat it? (I've stated that it was you )

2012/1/25 Eric Gjertsen < reply@reply.github.com

Hi Vanja, good talking with you the other day. Here's the transcript and etherpad of our session. To help improve the mentoring sessions, I'd really appreciate if you could fill out this survey when you get a chance.


Reply to this email directly or view it on GitHub:

https://github.com/mendicant-university/mentoring/issues/44#issuecomment-3652118

ericgj commented 12 years ago

Sorry, I didn't check it before - that's great, thanks.

elvanja commented 12 years ago

Hi Eric!

I apologize for bothering you, but I'm in a bit of conundrum. Namely, I'm struggling with "defer all decisions" idea and am trying to come up with a satisfying way to separate business/domain logic from delivery mechanism and database (or other boundaries). Building a system in an isolated environment has a strong appeal for me, especially regarding fast testing and the feedback it gives.

But, I can't seam to find a good solution to separating database layer from the system. I've tried different approaches but almost always I come up short for one reason or another.

I've put up a small github repo describing the problem and possible solutions to it @: https://github.com/elvanja/gedankenexperiment/tree/master/persistence_decoupling

I'm writing to you since I don't know where to look anymore, and it seamed kind of silly to put this on mendicant list. If you're busy or don't feel like looking into this, please don't feel obligated to help! I wouldn't want to impose...

Thanks! Vanja

ericgj commented 12 years ago

Hi Vanja,

Sure. Is there a particular project you are working on where you are exploring decoupling of persistence? It might help to focus on the constraints of a particular case rather than look for the 'best possible solution' for a general case. My view is that in simple cases, it's often overkill to decouple domain logic from persistence. So it's good to have a compelling case in front of us for where that would be useful.

You've may have seen this quote from Martin Fowler?

An OO domain model will often look similar to a database model, yet it will still have a lot of differences. A Domain Model mingles data and process, has multivalued attributes and a complex web of associations, and uses inheritance.

As a result I see two styles of Domain Model in the field. A simple Domain Model looks very much like the database design with mostly one domain object for each database table. A rich Domain Model can look different from the database design, with inheritance, strategies, and other [Gang of Four] patterns, and complex webs of interconnected objects. A rich Domain Model is better for more complex logic, but is harder to map to the database. A simple Domain Model can use Active Record, whereas a rich Domain Model requires Data Mapper.

BTW, it's not silly at all to put it on the mendicant list - definitely I'd encourage you to do that - especially since others besides me may have more/different experiences dealing with this question. And in any case it may lead to good discussions!

Eric

elvanja commented 12 years ago

Hi Eric,

first of all, thanks for the input :-) I can see the YAGNI working again :-) Alas, I can't show the real project, hence that sample/experiment repo and a bit contrived example.

Both Active Record or Data Mapper solutions is something I would like to postpone until the time comes. I'd really like to explore the idea of writing business layer upfront, attaching the DB and delivery mechanism at a later time. I kind of hope for this approach to let me freely develop my internal API, and just implement and attach the boundaries later.

I can see that these ideas are probably not communicated clearly, so I'll prepare another sample project, a bit more elaborated than the current, and post it to mendicant list... Let's see where it takes us then :-)

Thanks! Vanja

2012/3/21 Eric Gjertsen reply@reply.github.com:

Hi Vanja,

Sure. Is there a particular project you are working on where you are exploring decoupling of persistence? It might help to focus on the constraints of a particular case rather than look for the 'best possible solution' for a general case.  My view is that in simple cases, it's often overkill to decouple domain logic from persistence. So it's good to have a compelling case in front of us for where that would be useful.

You've may have seen this quote from Martin Fowler?

An OO domain model will often look similar to a database model, yet it will still have a lot of differences. A Domain Model mingles data and process, has multivalued attributes and a complex web of associations, and uses inheritance.

As a result I see two styles of Domain Model in the field. A simple Domain Model looks very much like the database design with mostly one domain object for each database table. A rich Domain Model can look different from the database design, with inheritance, strategies, and other [Gang of Four] patterns, and complex webs of interconnected objects. A rich Domain Model is better for more complex logic, but is harder to map to the database. A simple Domain Model can use Active Record, whereas a rich Domain Model requires Data Mapper.

BTW, it's not silly at all to put it on the mendicant list - definitely I'd encourage you to do that - especially since others besides me may have more/different experiences dealing with this question.  And in any case it may lead to good discussions!

Eric


Reply to this email directly or view it on GitHub: https://github.com/mendicant-university/mentoring/issues/44#issuecomment-4622631

elvanja commented 12 years ago

P.S. There seams to be a good topic going on at https://groups.google.com/forum/?fromgroups#!topic/objects-on-rails/TXAbqQRJ8iw (Objects on Rails book by Avdi Grimm group, topic "Plain Old Ruby Objects" trend) Also, the guys recommended some materials I'll be checking first. Just wanted to let you know...

2012/3/22 Vanja Radovanović elvanja@gmail.com:

Hi Eric,

first of all, thanks for the input :-) I can see the YAGNI working again :-) Alas, I can't show the real project, hence that sample/experiment repo and a bit contrived example.

Both Active Record or Data Mapper solutions is something I would like to postpone until the time comes. I'd really like to explore the idea of writing business layer upfront, attaching the DB and delivery mechanism at a later time. I kind of hope for this approach to let me freely develop my internal API, and just implement and attach the boundaries later.

I can see that these ideas are probably not communicated clearly, so I'll prepare another sample project, a bit more elaborated than the current, and post it to mendicant list... Let's see where it takes us then :-)

Thanks! Vanja

2012/3/21 Eric Gjertsen reply@reply.github.com:

Hi Vanja,

Sure. Is there a particular project you are working on where you are exploring decoupling of persistence? It might help to focus on the constraints of a particular case rather than look for the 'best possible solution' for a general case.  My view is that in simple cases, it's often overkill to decouple domain logic from persistence. So it's good to have a compelling case in front of us for where that would be useful.

You've may have seen this quote from Martin Fowler?

An OO domain model will often look similar to a database model, yet it will still have a lot of differences. A Domain Model mingles data and process, has multivalued attributes and a complex web of associations, and uses inheritance.

As a result I see two styles of Domain Model in the field. A simple Domain Model looks very much like the database design with mostly one domain object for each database table. A rich Domain Model can look different from the database design, with inheritance, strategies, and other [Gang of Four] patterns, and complex webs of interconnected objects. A rich Domain Model is better for more complex logic, but is harder to map to the database. A simple Domain Model can use Active Record, whereas a rich Domain Model requires Data Mapper.

BTW, it's not silly at all to put it on the mendicant list - definitely I'd encourage you to do that - especially since others besides me may have more/different experiences dealing with this question.  And in any case it may lead to good discussions!

Eric


Reply to this email directly or view it on GitHub: https://github.com/mendicant-university/mentoring/issues/44#issuecomment-4622631

ericgj commented 12 years ago

Thanks for that link - good discussion indeed. I was going to recommend Objects on Rails to you, but reading the chapter on POROs there it's kind of just scratching the surface, since Avdi stays within ActiveRecord and isn't so much focused on the interest in fast tests.

Both Active Record or Data Mapper solutions is something I would like to postpone until the time comes.

I understand. I didn't mean to suggest the ruby libraries by those names in quoting Fowler, but the patterns. I've used the DataMapper library, I think it's superior to AR in many ways, but it is still basically an ActiveRecord (pattern) implementation.

I'd really like to explore the idea of writing business layer upfront, attaching the DB and delivery mechanism at a later time. I kind of hope for this approach to let me freely develop my internal API, and just implement and attach the boundaries later.

It's not a bad approach at all, but what I see in your examples is that you are abstracting both the way your domain objects are persisted via data/repository object, and how your data objects themselves are persisted. In other words you are introducing a middle layer so that e.g. find_by_room_number and update are implemented differently depending on some lower-level component that talks to the data store. I think this is adding complexity that is not needed in most cases.

I don't know if it will help, but let me give you an example of what I would do. It's like a simplification of your repository_returning_objects. The data object(s) gets passed to the domain object via the constructor (Note I've changed the names of the classes so that the data object is named for the thing instead of the domain object):

    class ConferenceRoomBusinessRules

      attr_accessor :room, :from, :to

      def initialize(room)
        self.room = room
      end

      def book(from, to)
        self.from, self.to = from, to
      end

      def update
        room.update to_hash
      end

      def to_hash
        {:from => self.from, :to => self.to }
      end

    end

    class ConferenceRoom < ActiveRecord::Base       # if using AR, for example
    end

The only assumption ConferenceRoomBusinessRules makes in relation to its underlying storage then, is whatever it is passed in the constructor responds to #update with a hash argument. This is a very common idiom, used in the three major ruby ORMs (AR, DM, and Sequel), and probably countless other nosql libraries I don't know about.

(Note also the find is done by ConferenceRoom object itself, no need for the domain object to be responsible for that.)

So then in testing it would be very easy to design a mock object to pass in to the constructor, rather than one connected to a database.

Of course, as your domain objects get more complex, dealing with a graph of objects and associations etc., you start adding to the assumptions that the domain objects make in relation to the data objects they use. But I don't see how that can be helped.

What do you think?

elvanja commented 12 years ago

Hi! Sorry for a bit late response, I've had a hectic week...

Anyway, the example you gave looks fine. There are a few things I'm confused with however (not necessarily related to the example). I'll try to recapitulate here.

Initialization / Usage

If I am to use such objects in some client app (Rails, ...), there seams to be a bit too much choreography. For example, I'll need to write something like:

room = ConferenceRoom.new
booking = ConferenceRoomBusinessRules.new(room)
booking.book(from, to)

and I'll have to repeat that piece of code a number of times, at least once for every kind of business rule I have that needs persistence. I don't mind typing but it seems quite too much to read/comprehend. If there is a great number of business rules, I feel this soon becomes hard to read.

What I'd like to have is some cleaner/more readable way of doing things. Maybe something like

ConferenceRoomBusinessRules.book(from, to)

At least this seams much more straight forward / understandable. (I would actually use ConferenceRoom instead of ConferenceRoomBusinessRules here)

Appending behaviour

Also, I really like the idea of Williams, Master of the "Come From". It seems natural to describe what you want a class to do (business object / rule), and then attach other non-business related behaviour to it. Hence the Persistence appended to domain. I imagine the client app to be able to do something like:

#once in client app setup
require 'ConferenceRoomBusinessRules'
require 'ConferenceRoomPersistence'

#usage in client app
rooms = ConferenceRoom.find_by_number_of_places(booking.people_count)
...
room.book(from, to)

It's like I'd like to see only business related code in every place but initialization routine.

Injecting persistence

If I am to use dependency injection, maybe the solution would be to have one context class/object, from which all the rest is called. Then, maybe I could do something like:

#once in client app setup
persistence = ConferencePersistence.new(initialization_options)
domain = ConferenceRoomBusinessRules.new(persistence)

#usage in client app
room = domain.new_room #how would otherwise persistence get the needed connection data?
...

I can't get out of the solution where my domain rules must be able to generate all the classes, like some kind of general factory. It is possible I am missing some point here so feel free to correct me :-)

Persistence API

Don't know how to call this section, but OK... I'd like to have the persistence API needed by the business rules objects documented in a sense that I could extract the needed API at any time and implement it (test version, production version, ...). I know there are always tests, but I feel that there is a lack of a comprehensive overview of all the persistence methods needed for the business side to work. That is the reason I'd like to encapsulate all the persistence calls in such placeholder (proxy?) objects. And that is why I created a separate layer that serves actually as a placeholder for that persistence API. As a side effect, it becomes rather nice to have all the persistence calls in one place / documented. I kind of feel lost when e.g. I have a number of finders (findby...) and don't really have a control which of them are called on the persistence layer and when or from where...

Here the only thing that bothers me is that certain methods are needed in business layer but in persistence too, like in Persistence appended to domain. This approach gets a bit mixed up with the idea of appending persistence since it uses for exeample find_by_room_number and it will get that method only later in persistence.

All in all, I am baffled with all the variety of options and seemingly opposing ideas. Hope I didn't bore you with the amount of text :-)

ericgj commented 12 years ago

Hi Vanja,

I am having a hard time understanding some of the motivations here, except in a general way. To go back to your original point re "defer all decisions" -- if you are stuck on the question of persistence, or more precisely the question of how to decouple persistence from your models, maybe the best thing to do is to ignore it for now. Pretend you don't need persistence at all, and spec out how your models are supposed to interact.

For instance, you've got conference rooms, participants, times the rooms can be booked, limitations on how many people fit in which rooms, etc. Figure out what questions you want to be able to answer given various states of these entities -- ignoring how these entities are found or updated. Draft some specs you can then implement the rules against. Or maybe you've already done this?

In other words, my suggestion is to let the domain guide you. Once you have that in place, you'll have fast unit tests of the core functionality. Then various options for hooking up persistence can be tried out. That's the order I would do it. Otherwise, my experience is that looking for the perfect division of responsibility in the abstract, you'll end up with very difficult to understand code, which may or may not be what you actually need in this particular case.

Hope this helps! Eric

elvanja commented 12 years ago

Hi,

Well, I feel the pain of having hard time expressing these ideas, sorry to have inflicted them on you :-) I've already done the specs and all this was "inspired" by the lack of idea on how to proceed. Namely I wanted to create a feature limited but working instance, that could actually be seen/tried. The domain rules are already in place, an working according to specs (and user stories).

Anyway, you are (as usual I might add!!!) right, it is just possible that I've prepared the domain objects too much in relation to persistence. I'll have to try to untangle that mix and perhaps use the approach you mentioned earlier.

All in all, when I manage to make this idea work, I hope I'll manage to make a sample app, maybe post it somewhere an serve as a better point to this discussion :-) Thanks again for your help, much appretiated!

ericgj commented 12 years ago

Not at all, it was interesting to read through your ideas, and to see the various conversations going on about decoupling persistence. Don't feel bad about not having "solved" it, it seems lots of folks are working on related problems, and it doesn't seem like there's anything approaching consensus on "best practices". Look forward to seeing what you work out! And definitely feel free to drop a note to the mendicant list, or on IRC.

ericgj commented 12 years ago

Hey Vanja, you may have seen this already - I had seen it awhile back but forgot about it. Making ActiveRecord Models Thin It's similar to the approach I sketched above, except he has the domain model also wrap find method(s), as you wanted to do as well. It seems generally like a good approach to me - then your controllers don't have to know anything about the AR layer.

elvanja commented 12 years ago

Hi! Thanks for the link, great help. I came upon this too: https://gist.github.com/2217602 Still no time to investigate this further, uh.... Thanks again :-)

2012/3/30 Eric Gjertsen reply@reply.github.com:

Hey Vanja, you may have seen this already - I had seen it awhile back but forgot about it. Making ActiveRecord Models Thin It's similar to the approach I sketched above, except he has the domain model also wrap find method(s), as you wanted to do as well. It seems generally like a good approach to me - then your controllers don't have to know anything about the AR layer.


Reply to this email directly or view it on GitHub: https://github.com/mendicant-university/mentoring/issues/44#issuecomment-4835633