Hey James,

"the webserver being a client" is really very simple. Basically instead of 
starting one "app" you start two. Your actual "app" and the "web-app" that 
depends on "app". One contains your business logic and the other everything 
related to translating HTTP to app API calls. "app" doesn't know about the 
web part.

The component stuff also assumes that the web server itself is not a 
component. A typical Servlet Container is built this way, it assumes that 
it will host your app and not the other way around. I use http-kit but I 
still want that hierarchy. Trying to turn the web server itself into 
component just produces nightmares of cyclic dependencies and such.

Can't really explain this very well, it is way too hot to think straight.

I wanted to create a proper library out of my component stuff for a while 
but never get around to it. I might do that some day to create an actual 
example I can refer to.

Cheers,
/thomas


On Friday, July 3, 2015 at 9:39:24 AM UTC+2, James Henderson wrote:
>
> Hey Thomas, thanks for your e-mail :)
>
> On Monday, 29 June 2015 11:25:44 UTC+1, Thomas Heller wrote:
>>
>> Hey,
>>
>> interesting approach but I don't like the nesting and "manual" wiring of 
>> dependencies. 
>>
>
> I've found people at both ends of that particular spectrum - some that 
> won't live with DI, some that won't live without it :) I guess a library 
> like Yo-yo has two options - either be opinionated about it, or let people 
> choose one or the other. In this case, I've chosen to let people choose - 
> there's nothing about Yo-yo that mandates the nesting (except the top-level 
> function) - what you do within that is up to you.
>  
>
>> I don't quite like that every with-* function remains on the stack as 
>> well, but it shouldn't hurt that much. 
>>
>
> Hmm - I was wondering about that too. Maybe an approach similar to 
> trampoline would help here?
>  
>
>> An uncaught exception will also take down your entire system, but I guess 
>> you'd have a try/catch in your "latch" anyways.
>>
>
> I'm not sure it will? If there's an exception thrown during system 
> startup, the components will then have an opportunity to stop themselves 
> (in the reverse order) because of their try/finally's - I'd say this is the 
> behaviour we'd want, in order to avoid half-started systems. Once the 
> system's started, and (latch) called, an uncaught exception in the 
> components won't stop the system - because it'll be thrown on a different 
> thread, if I understand correctly? Certainly need to write some test cases 
> around it!
>  
>
>>
>> But what I miss the most is an instance of your "app" (ie. all components 
>> together). You create it yourself in the example but I really want that 
>> always. Sometimes you just want to access your system from the outside just 
>> to see whats up (eg. REPL into a live system). I also consider the 
>> webserver to be a "client" of my "app" and not part of it (or another layer 
>> of it if you will), but that is a topic for another day.
>>
>>
> Yep, I agree with this - I've been using some workarounds to get values 
> out of the system, none of them particularly pretty. Interesting idea about 
> the webserver being a client of the app - would be good to see where you 
> take that?
>
>
>> Way way back in the day I used to work with (and on) PicoContainer which 
>> was/is a dependency injection and lifecycle management container. I tried 
>> writing a DSL for it (in Groovy, this was 2003 or so) but concluded that 
>> Java already was good enough to set everything up, a DSL (or XML) is 
>> overkill. All you need to describe a "Component" is:
>>
>> a) what are its dependencies
>> b) how do I start it
>> c) how do I stop it
>>
>> In that light I wrote my own "dependency injection" helper functions 
>> since nothing like Stuart's Component existed at the time I got into 
>> Clojure. I don't like Component due to its invasive protocol but in essence 
>> I do the same.
>>
>> In my system I just set up a map of components and use that as a 
>> descriptor for wiring:
>>
>> {:a {:depends-on []
>>      :start my.components.a/start
>>      :stop my.components.a/stop}
>>  :b {:depends-on [:a]
>>      :start my.components.b/start
>>      :stop my.components.b/stop}}
>>      
>> The key in the outer map becomes whatever the :start function returns and 
>> is refered to it by its name :a (the key of the map). The :start function 
>> of :b is called as (my.components.b/start instance-of-a). An instance of a 
>> component is treated as an opaque value and other components interact with 
>> it only via its "public" interface (ie. my.components.a). Whether this is 
>> done via a protocol or not doesn't matter. When a shutdown is requested the 
>> :stop function is called with the instance of the component as the argument.
>>
>> That is about it. Mocking is just assoc over default descriptor map and I 
>> have helper functions to only do partial start/stop calls if only a 
>> specific component is needed (eg. I only need :a).
>>
>> Like I said it basically does the same stuff as Component, just a little 
>> less invasive since I think a component should not know about the container 
>> it runs in.
>>
>
> Looks another interesting approach :) I'm currently hacking on some 
> similar ideas myself - think there's plenty of room for iteration in this 
> area at the moment!
>  
>
>> Hope that was somewhat useful as feedback to Yo-Yo.
>>
>
> Certainly was! Thanks! :)
>  
>
>>
>> Cheers,
>> /thomas
>>
>>
>> On Sunday, June 28, 2015 at 4:03:34 PM UTC+2, James Henderson wrote:
>>>
>>> As promised, have blogged: 'Yo-yo & Component - Side by Side 
>>> <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org>
>>> '
>>>
>>> Contents:
>>>
>>>
>>>    - Making components 
>>>    
>>> <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#making-components>
>>>    - Using a component as a dependency 
>>>    
>>> <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#using-a-component-as-a-dependency>
>>>    - Serving a REST API 
>>>    
>>> <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#serving-a-rest-api>
>>>    - Wiring it all up 
>>>    
>>> <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#wiring-it-all-up>
>>>    - Yo-yo / Component Interoperability 
>>>    
>>> <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#yo-yocomponent-interoperability>
>>>    - Mockable Services 
>>>    
>>> <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#mockable-services>
>>>    - ‘Mocking out’ dependencies 
>>>    
>>> <https://github.com/james-henderson/yoyo/blob/master/articles/mocking-out-dependencies>
>>>
>>> Let me know what you think!
>>>
>>> Cheers,
>>>
>>> James
>>>
>>> On Thursday, 25 June 2015 09:25:56 UTC+1, James Henderson wrote:
>>>>
>>>> Seems like the next step for this would be for me to put together a 
>>>> blog with an example Component system, and its equivalent Yoyo system?! :) 
>>>> Should have time for that over the weekend.
>>>>
>>>> James
>>>>
>>>> On Thursday, 25 June 2015 09:05:39 UTC+1, James Henderson wrote:
>>>>>
>>>>>
>>>>>
>>>>> On Wednesday, 24 June 2015 11:17:41 UTC+1, Atamert Ölçgen wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Tue, Jun 23, 2015 at 11:47 PM, James Henderson <
>>>>>> ja...@jarohen.me.uk> wrote:
>>>>>>
>>>>>>> Hi Atamert - thanks :)
>>>>>>>
>>>>>>> I thought it might be preferable to keep the call to (latch)explicit 
>>>>>>> - it means that ylet can be used in nested calls, too - for 
>>>>>>> example, to set up and compose groups of components/sub-systems: 
>>>>>>> (contrived 
>>>>>>> example, though!)
>>>>>>>
>>>>>>> ;; (docs for ylet at 
>>>>>>> https://github.com/james-henderson/yoyo#introducing-ylet )
>>>>>>>
>>>>>>> (require '[yoyo :refer [ylet]])
>>>>>>>  
>>>>>>> (defn with-connections [config f]
>>>>>>>   (ylet [db-pool (with-db-pool (:db config))
>>>>>>>          es-conn (with-es-connection (:elasticsearch config))]
>>>>>>>  
>>>>>>>     (f {:db-pool db-pool
>>>>>>>         :es-conn es-conn})))
>>>>>>>  
>>>>>>> (defn make-system [latch]
>>>>>>>   (let [config ...]
>>>>>>>     (ylet [connections (with-connections system)
>>>>>>>            _ (with-webserver {:handler (make-handler (merge connections
>>>>>>>                                                             {:config 
>>>>>>> config}))
>>>>>>>                               :port 3000})]
>>>>>>>       (latch))))
>>>>>>>
>>>>>>>
>>>>>>> How would you see the with-* functions working, btw?
>>>>>>>
>>>>>>
>>>>>> I think the general idea should be to provide a clean API to the 
>>>>>> consumer (of your lib). Perhaps something that accepts a start function, 
>>>>>> a 
>>>>>> stop function and some sort of main loop (f in your example).
>>>>>>
>>>>>
>>>>> Not sure I understand what you mean here? Tbh, I was trying to get 
>>>>> away from the idea of separate start & stop functions - it seems 
>>>>> 'cleaner' 
>>>>> to me without them! (although of course that's subjective). 
>>>>>
>>>>> Also, the 'with-*' functions here are consumer code - the only Yo-yo 
>>>>> functions/macros in this example are 'run-system!' and 'ylet'. Yo-yo 
>>>>> itself 
>>>>> is *tiny* (<100 LoC) - my aim was for a library that solely dealt 
>>>>> with starting/stopping a provided system, and *no more* :)
>>>>>
>>>>> Maybe it'd be worth fleshing out an example of what you were looking 
>>>>> for?
>>>>>
>>>>> Cheers,
>>>>>
>>>>> James
>>>>>
>>>>>  
>>>>>>
>>>>>>>
>>>>>>> Cheers,
>>>>>>>
>>>>>>> James
>>>>>>>
>>>>>>> On Tuesday, 23 June 2015 09:57:16 UTC+1, Atamert Ölçgen wrote:
>>>>>>>>
>>>>>>>> Hi James,
>>>>>>>>
>>>>>>>> Interesting idea. Thanks for sharing.
>>>>>>>>
>>>>>>>> I think you can simplify this:
>>>>>>>>
>>>>>>>> (yoyo/run-system!
>>>>>>>>  (fn [latch]
>>>>>>>>    (ylet [db-pool (with-db-pool {...})
>>>>>>>>           :let [server-opts {:handler (make-handler {:db-pool 
>>>>>>>> db-pool})
>>>>>>>>                              :port 3000}]
>>>>>>>>           web-server (with-web-server server-opts)]
>>>>>>>>      (do-this web-server)
>>>>>>>>      (do-that db-pool web-server)
>>>>>>>>      (latch))))
>>>>>>>>
>>>>>>>>
>>>>>>>> to:
>>>>>>>>
>>>>>>>> (yoyo/foo! [db-pool (with-db-pool {...})
>>>>>>>>             :let [server-opts {:handler (make-handler {:db-pool 
>>>>>>>> db-pool})
>>>>>>>>                                :port 3000}]
>>>>>>>>             web-server (with-web-server server-opts)]
>>>>>>>>   (do-this web-server)
>>>>>>>>   (do-that db-pool web-server))
>>>>>>>>
>>>>>>>>
>>>>>>>> I believe with-* function can also be simplified further.
>>>>>>>>
>>>>>>>>
>>>>>>>> On Tue, Jun 23, 2015 at 1:18 AM, James Henderson <
>>>>>>>> ja...@jarohen.me.uk> wrote:
>>>>>>>>
>>>>>>>>> Hi all,
>>>>>>>>>
>>>>>>>>> I've just released an early version of 'Yo-yo', a protocol-less, 
>>>>>>>>> function composition-based alternative to Component. It's still in 
>>>>>>>>> its 
>>>>>>>>> early stages, so feedback would be very much appreciated!
>>>>>>>>>
>>>>>>>>> https://github.com/james-henderson/yoyo
>>>>>>>>>
>>>>>>>>> Yo-yo was also an experiment to see what could be de-coupled from 
>>>>>>>>> the concept of 'reloadable systems', so you won't find any 
>>>>>>>>> configuration, 
>>>>>>>>> dependency injection, etc - just a way to write a system that can be 
>>>>>>>>> easily 
>>>>>>>>> started, stopped, and reloaded.
>>>>>>>>>
>>>>>>>>> Fundamentally, we start by assuming there's a function available 
>>>>>>>>> that only returns 'when the system stops' - a 'latch', say. If we had 
>>>>>>>>> such 
>>>>>>>>> a function, we could start our system, call that function, then stop 
>>>>>>>>> the 
>>>>>>>>> system (closing any necessary resources). A database pool, for 
>>>>>>>>> example, 
>>>>>>>>> might look like this:
>>>>>>>>>
>>>>>>>>> (defn with-db-pool [db-config f]
>>>>>>>>>   (let [db-pool (start-pool! db-config)]
>>>>>>>>>     (try
>>>>>>>>>       (f db-pool)
>>>>>>>>>
>>>>>>>>>       (finally
>>>>>>>>>         (stop-pool! db-pool)))))
>>>>>>>>>
>>>>>>>>> Here, we're assuming that we'll be passed 'f', the 'latch' 
>>>>>>>>> function. A web server would be similar, and, because they're both 
>>>>>>>>> functions, they're very simple to compose:
>>>>>>>>>
>>>>>>>>> (with-db-pool {...}
>>>>>>>>>   (fn [db-pool]
>>>>>>>>>     (with-web-server {:handler (make-handler {:db-pool db-pool})
>>>>>>>>>                       :port ...}
>>>>>>>>>       (fn [web-server]
>>>>>>>>>         ;; TODO: Ah. We've run out of turtles. :(
>>>>>>>>>         ))))
>>>>>>>>>
>>>>>>>>> This is where Yo-yo comes in - there’s a function called 
>>>>>>>>> run-system!, which takes a function that accepts a latch:
>>>>>>>>>
>>>>>>>>> (:require [yoyo])
>>>>>>>>>
>>>>>>>>> (yoyo/run-system!
>>>>>>>>>   (fn [latch]
>>>>>>>>>     (with-db-pool {...}
>>>>>>>>>       (fn [db-pool]
>>>>>>>>>         (with-web-server {:handler (make-handler {:db-pool db-pool}) 
>>>>>>>>> ; n.b. we have access to the db-pool here - no need for global state!
>>>>>>>>>                           :port ...}
>>>>>>>>>           (fn [web-server]
>>>>>>>>>             (latch))))))) ; Aha!
>>>>>>>>>
>>>>>>>>> run-system! then returns a promise - deliver any value to it, and 
>>>>>>>>> it'll stop the system.
>>>>>>>>>
>>>>>>>>> And that's pretty much it! There are a few more functions - mostly 
>>>>>>>>> to do with easily starting/stopping/reloading a system through the 
>>>>>>>>> REPL, 
>>>>>>>>> and a macro to simplify the 'function staircase' - these are covered 
>>>>>>>>> in 
>>>>>>>>> more detail in the README. There are some also common components - a 
>>>>>>>>> database pool, a web server, and a simple integration for existing 
>>>>>>>>> Component systems.
>>>>>>>>>
>>>>>>>>> It'd be great to hear your thoughts/ideas, whatever they may be - 
>>>>>>>>> either through here, e-mail, Github, or Twitter - thanks!
>>>>>>>>>
>>>>>>>>> James
>>>>>>>>>
>>>>>>>>>  -- 
>>>>>>>>> You received this message because you are subscribed to the Google
>>>>>>>>> Groups "Clojure" group.
>>>>>>>>> To post to this group, send email to clo...@googlegroups.com
>>>>>>>>> Note that posts from new members are moderated - please be patient 
>>>>>>>>> with your first post.
>>>>>>>>> To unsubscribe from this group, send email to
>>>>>>>>> clojure+u...@googlegroups.com
>>>>>>>>> For more options, visit this group at
>>>>>>>>> http://groups.google.com/group/clojure?hl=en
>>>>>>>>> --- 
>>>>>>>>> You received this message because you are subscribed to the Google 
>>>>>>>>> Groups "Clojure" group.
>>>>>>>>> To unsubscribe from this group and stop receiving emails from it, 
>>>>>>>>> send an email to clojure+u...@googlegroups.com.
>>>>>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> -- 
>>>>>>>> Kind Regards,
>>>>>>>> Atamert Ölçgen
>>>>>>>>
>>>>>>>> ◻◼◻
>>>>>>>> ◻◻◼
>>>>>>>> ◼◼◼
>>>>>>>>
>>>>>>>> www.muhuk.com
>>>>>>>>  
>>>>>>>  -- 
>>>>>>> You received this message because you are subscribed to the Google
>>>>>>> Groups "Clojure" group.
>>>>>>> To post to this group, send email to clo...@googlegroups.com
>>>>>>> Note that posts from new members are moderated - please be patient 
>>>>>>> with your first post.
>>>>>>> To unsubscribe from this group, send email to
>>>>>>> clojure+u...@googlegroups.com
>>>>>>> For more options, visit this group at
>>>>>>> http://groups.google.com/group/clojure?hl=en
>>>>>>> --- 
>>>>>>> You received this message because you are subscribed to the Google 
>>>>>>> Groups "Clojure" group.
>>>>>>> To unsubscribe from this group and stop receiving emails from it, 
>>>>>>> send an email to clojure+u...@googlegroups.com.
>>>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> -- 
>>>>>> Kind Regards,
>>>>>> Atamert Ölçgen
>>>>>>
>>>>>> ◻◼◻
>>>>>> ◻◻◼
>>>>>> ◼◼◼
>>>>>>
>>>>>> www.muhuk.com
>>>>>>  
>>>>>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to