Hi Colin, for me this is usually an Invented Here/Not Invented Here
question.

When I'm inventing a thing with state and a lifecycle I'll define it as a
Record and slap the Lifecycle protocol implementation onto it. I'd do that
with your Registry.

When I'm using a thing someone else made, usually that thing will have its
own state and implementation encapsulation and its own lifecycle, so I'll
make a simple Component only to manage it. I won't have that Component play
any other privileged role in regards to accessing the NIH thing or getting
to its internal state. Database connection is a canonical example- as often
discussed functions that do something with a database should take a
connection parameter, not a Component parameter from which they have to
extract a connection.

That all said, sometimes I'll have multiple implementations of Invented
Here things that the app needs just one of at runtime- say, Registry
implementations backed by different underlying technology that are
appropriate for different application use cases. In that case it starts to
smell like an NIH thing and I'll see if I can treat it that way and make a
single Component that can be told which Registry to manage. Sometimes that
works, but sometimes it makes sense to extend the protocol to the
individual implementations.

Jonah



On Mon, Mar 2, 2015 at 6:32 PM, Dylan Butman <dbut...@gmail.com> wrote:

> I would do the latter.
>
> I like the extend types to component/Lifecycle wherever possible. The goal
> is to have all components in your system only interact via protocols. This
> way, you have established interfaces between components, and if you want to
> swap an implementation, you just satisfy the protocol. So adapting your
> example
>
> (defprotocol IRegistry
>   (register-with [registry cb spec]))
>
> I also like to provide all reference as arguments instead of assoc'ing
> them inside component/start. This allows them to be more easily used in
> other parts of the system, and gives you the opportunity to validate the
> references, continuing the idea of protocol interface
>
> (defn validate-system
>   "validate the portion of the system contained within the component"
>   [schema cmp]
>   (s/validate {s/Keyword s/Any} schema)
>   (->> (select-keys cmp (keys schema))
>        (s/validate schema)))
>
> (defrecord Registry [state]
>   Lifecycle
>   (start [this]
>     (validate-system {:state clojure.lang.Atom} this) ;; this could be
> more general, let's be specific though
>     ;; start the registry
>     this)
>   (stop [this]
>     ;; stop the registry
>     this)
>   IRegistry
>   (register-with [registry cb spec]
>     (swap! state :assoc cb spec)))
>
> (defrecrod UsesRegistry [registry]
>   Lifecycle
>   (start [this]
>          (validate-system {:registry (s/pred (partial satisfies?
> IRegistry))} this)
>
>          this)
>   (stop [this]
>         this))
>
>
> On Sunday, March 1, 2015 at 7:50:58 AM UTC-5, Colin Yates wrote:
>>
>> If I have a stateful thing with a lifecycle then is the system component
>> the instance of the thing or a wrapper that contains the thing.
>>
>> For example, let's say I have a registry of clients that want to be
>> polled then I might have the following:
>>
>> (defrecord Registry [state])
>> (defn register-with [registry cb spec] (swap! (:state registry) :assoc cb
>> spec))
>> (defn start [registry] ...)
>> (defn stop [registry] ...)
>> (den some-other-thing [..] ....)
>>
>> Great.
>>
>> Now I want to expose that and use Stuart's excellent component library
>> (which I really wish a global 'everything has been defined, now the actual
>> system is starting' lifecycle event, but anyway). I seem to find most
>> examples do:
>>
>> (defrecord RegistryComponent []
>>   component/Lifecycle
>>   (start [this]
>>     (let [registry (->Registry (atom {}))]
>>       (start registry)
>>       (assoc this :registry registry))
>>   (stop [this]
>>      (stop (:registry this))
>>      (assoc this :registry nil)))
>>
>> Other components get the RegistryComponent but then have to unravel the
>> actual :registry.
>>
>> Another approach would be to adjust the actual Registry and that that
>> implement the lifecycle:
>>
>> (defn register-with [registry cb spec] (swap! (:state registry) :assoc cb
>> spec))
>> (defn start [registry] ...)
>> (defn stop [registry] ...)
>> (den some-other-thing [..] ....)
>> (defrecord Registry [state]
>>   component/Lifecycle
>>   (start [this] (start this) this)
>>   (stop [this] (stop this) this))
>>
>> On the one hand, the Lifecycle component can be viewed as separate from
>> the thing it is managing, and this wrapping and unwrapping is only a
>> concern of Components. Nothing outside of a Lifecycle instance worries
>> about it and nothing ever receives a Lifecycle instance.
>>
>> On the other hand, it all felt a little bit OO-ish - and actually I don't
>> quite see the 'vanilla' Registry implementing the Lifecycle component as
>> complecting anything inappropriate. The Lifecycle instance is only
>> describing behaviour - there is no separate 'thing'.
>>
>> Is that correct or have I missed something? What do you all do?
>>
>>  --
> 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.
>

-- 
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