Phoenix v1.3.0-rc.0 released

Yes, it is. Setting up references automatically can lead to wrong data being exposed or modified.

4 Likes

@josevalim Is authentication/ authorization an application or interface responsibility? In my opinion it would make much more sense to build it into the application’s api, as the app would then be self-contained and provide consistent functionality across interfaces.

{:ok, token} = App.Auth.login(email, password)
{:ok, post} = App.Blog.create_post(token, attrs)
{:error, :unauthorized} = App.Admin.destroy_application(token)
App.Auth.logout(token)
{:error, :unauthenticated} = App.Blog.create_post(token, attrs)

## vs. ##

plug :ensure_authenticated
plug :ensure_authorized
{:ok, post} = App.Blog.create_post(user_id, attrs)

Also the approach that contexts shouldn’t know about other resources surpresses the use of Elixir’s pattern matching capabilities, is this an issue or a feature?

App.Blog.create_post(user_id, attrs)

## vs ##

App.Blog.create_post(%App.Auth.User{}, attrs)

Edit: Better example

App.Fleet.create_car_for_employee(employee_id, attrs)
App.Fleet.create_car_for_manager(manager_id, attrs)

## vs ##

App.Fleet.create_car(%App.HR.Employee{}, attrs)
App.Fleet.create_car(%App.HR.Manager{}, attrs)
2 Likes

Design it in your app and see what feels better. We are not going to arrive to conclusions based on pseudo-code. :slight_smile:

A context can know about resources in other contexts, as long as those resources are explicitly returned by the context (i.e. as long as you respect the code boundaries).

5 Likes

Where would common “models” go? As an example, address model is the same and is shared by user, company, employee. What are the suggested alternatives for such a use case with the new paradigm? Love the new paradigm and thanks everyone on the elixir/phoenix team that help guide the community towards better ways. One way I can think of is to push it into a “communications” context and use it that way. Any other ideas that are better are welcome.

Also, where do models that cut across domains fit? Example, accounts has the user model (if I may still call it that). A company model lives somewhere. A user can belong to a company with a certain role. If we assume company is a different context (and not the accounts context), then role model (a users role within a company) fits where? - maybe in a new context called “user_roles”?

2 Likes

It is the same as in any other Elixir project. If you need to have functions or data structures that are shared across multiple contexts, you can define it at lib/my_app/foo.ex or lib/my_app/shared/foo.ex and just have everyone depend on it.

Btw, Phoenix does not have models from v1.3 and Ecto does not have models for a long while already. You probably meant to say schemas but remember schemas are simply data structures with type information attached to each field.

2 Likes

Nice work with this!

A little idea for non-umbrella apps, what if the web folder was moved up one level to be placed directly into lib and renamed into myapp_web. That would bring it little bit closer to the folder structure of umbrella apps.

lib
└── site
    ├── application.ex
    ├── blog
    │   ├── author.ex
    │   └── blog.ex
    ├── repo.ex
    └── web
        ├── channels
        │   └── user_socket.ex
        ├── controllers
        │   ├── author_controller.ex
        │   └── page_controller.ex


lib
├── site
│   ├── application.ex
│   ├── blog
│   │   ├── author.ex
│   │   └── blog.ex
│   └── repo.ex
└── site_web
    ├── channels
    │   └── user_socket.ex
    ├── controllers
    │   ├── author_controller.ex
    │   └── page_controller.ex
6 Likes

Thanks @josevalim

I used the term models because I am still stuck in phoenix 1.2 land :slight_smile:
Thanks for correcting me.

Would it make more sense to use something like “shared” or have some new context - like “communications” for things like address? Or maybe create separate “business_address”, “user_address” and “employee_address” in their respective domains but use macros to bring in the fields from an address module defined in some common location? Just trying to get a few ideas. I know there is no hard and fast rule, so if you have any rules of thumb it would be great to know. Otherwise, thanks again for everything you do.

2 Likes

@chrismccord, @josevalim, @wojtekmach and others many thanks for your great work guys, you’re awesome!
Why did you decide to prefix schema and DB table names with context name in generators? Shouldn’t we separate a DB from application logic? If we ever change a context name or structure, we will have to rename all the tables related to this context. One schema probably can be related to multiple contexts. Multiple applications probably can use the same DB tables with different contexts in minds.

4 Likes

I had the same question: Phoenix v1.3.0-rc.0 released

I decided to strip the context prefix from the database table name. To me, contexts are limited to the app and shouldn’t bleed into the database.

4 Likes

Just a quick update, we’ve recently converted hex.pm to follow v1.3 project structure [1]. There’s still more to be done (using FallbackController, moving some functions around etc) but it should give you an idea how the new structure works in practice. Feedback & PRs are welcome!

[1] https://github.com/hexpm/hexpm

6 Likes

Cool idea, I’m not sure if this particular approach was discussed before. It would indeed make it easier to convert a non-umbrella project to an umbrella one down the road
(literally: mv lib/myapp_web apps/myapp_web/lib/ +/- project boilerplate & git commands)

edit: I posted this too hastily, it isn’t any more easy than mv lib/myapp/web apps/myapp_web/lib/myapp_web :stuck_out_tongue: but I still kinda like that it’s less nested.

3 Likes

Just wanted to drop my 2 cents for the directory structure.

I think, this depends on the perspective from which you’re discovering the project.

If you’re browsing through the directories (eg. in your editor’s navigation panel), then I guest it’s quite easy to understand that the lib/accounts/accounts.ex is the main entry point (because the file is named the same way as the directory).

On the other hand, if your starting point of exploration is the source code, eg. exception message like

** (UndefinedFunctionError) function Accounts.create_transaction/0 is undefined or private.

you might start looking for file lib/accounts.ex, which doesn’t exist.

I guess, for shallow directory structure (like the one presented here) this might not be too much of a problem, but is this something that will scale well if it gets more nested? (PS. I also accept, that this might be a smell that should make one think about refactoring, thou).

Bottom line is - both Accounts.Foo and Accounts modules are defined in lib/accounts/ - in this context, module name builds quite valuable expectation, which unmet, might cause confusion.

I think, convention, is something that will help you in longer term. Not only for people maintaining projects, but also newcomers starting their journey with Elixir/Phoenix.

3 Likes

Web.Plugs is still defined in web/plugs.ex (as it should be).

I don’t get where this change came from, really. It’s not as if it changes anything, but the namespaces and file structure had a one to one mapping before and now they suddenly don’t. For what reason, exactly?

2 Likes

perhaps you somehow looked at outdated code, but we have Hexpm.Web.Plugs defined in lib/hexpm/web/plugs.ex [1] and so the module name perfectly matches the filename.

[1] https://github.com/hexpm/hexpm/blob/master/lib/hexpm/web/plugs.ex#L1

1 Like

@chrismccord What do you think about this idea?

1 Like

Yes, that is exactly what I pointed out. What makes Plugs privileged so it doesn’t follow this new, odd structure (base namespace module defined in the folder for the namespace)? plugs.ex should, according to this new idea, be placed in plugs/.

I was pointing out that it’s exactly where it should be, but not where this new convention says it should be.

This is assuming I haven’t misunderstood the new convention to only apply to lib/* directories, in which case it’s actually even worse, as it’s not even internally consistent.

1 Like

OK, I see what you mean, I didn’t make the connection that plugs.ex could indeed be in lib/hexpm/web/plugs/, that’s a good point. It does make sense to me to move it there, I’d also like to give this module a more meaningful name, but nothing pops up yet.

I was pointing out that it’s exactly where it should be, but not where this new convention says it should be.

As was mentioned in this topic a couple of times (e.g. [1] [2]), Elixir doesn’t care where you place your files. If you don’t like the new structure nobody forces you to use it. I’d say, follow this convention as much or as little as you want.

To give a concrete example from this refactoring effort, I particularly like how e.g. Hexpm.CDN* [3] modules are currently placed. Note this context is a bit different from e.g. Accounts context (which uses Repo, Schemas etc), but similar in a way that it represents a slice of the application. This set of modules is totally independent of Hex.pm, it could easily be a separate package, and so I quite like that all files are in the same directory. Does it break conventions in file structures compared to projects like Ecto, Poison etc? Yes. Does it make it easier for me to navigate Hex.pm project? Yes!

[1] Phoenix v1.3.0-rc.0 released
[2] Phoenix v1.3.0-rc.0 released
[3] https://github.com/hexpm/hexpm/tree/master/lib/hexpm/cdn

3 Likes

I see your point and I’ll have it in mind. I feel awkward about it now, though, because it’ll still feel like I’m now going against convention. The community is likely to adapt one or the other and I usually need good reasons to break convention.

Anyway, thanks for the rationale and example. It’s something to keep in mind.

2 Likes

@chrismccord the way generators now put the changeset functions into context file and not in the schema itself breaks the contract expected by Ecto’s cast_assoc. Since cast_assoc is almost always expected in any most changeset, having the changesets in context file requires users to define additional changesets in schema file just for the association or use the with option in cast_assoc. I think moving the changeset functions to schema can reduce the noise in context file and at the same time relive developers from defining changesets for associations.

2 Likes

That’s a very good point we have not considered before. Can you please open up an issue so we are sure to discuss it?

1 Like