Moneygun - Multitenancy & Teams boilerplate | SupeRails #208

30/10/2024
moneygun Mulitenancy saas saas boilerplate
  • Teams should be an MVP feature!
  • Invite users to organizations
  • Role-based access
  • Switch between organizations
  • Different sidebars
  • Inspect Gemfile
  • Super Scaffolding

In Episode 115 we created a multi-tenant application without any gems.

Multitenancy was row-based, and we set current tenant via routes.

Episode 115: https://superails.com/posts/rails-7-115-multitenancy-teams-and-roles-without-a-gem

Based on this I created a well-tested boilerplate multi-tenant application.

The name is Moneygun.

Why? Because adding this architecture early in your app development will help you save & make a lot of money!

Save money - on future refactoring.

Make money - on making your apps business-friendly early.

0:00 Teams should be an MVP feature!
2:50 Invite users to organizations
5:20 Role-based access
6:52 Switch between organizations
8:00 Different sidebars
8:30 Inspect Gemfile
9:29 Super Scaffolding

Moneygun source code: https://github.com/yshmarov/moneygun

Transcript
Hello friends. In episode 1 1 15, I talked about building a basic multitenant application in Rails. You can think of Multitenancy as if you, as the application owner are a landlord. You own an apartment building and individuals, organizations who rent a space inside your bot building, like for the monthly fee are the tenants over the apartment. So that's more or less how software as a service works where you as the application owner, uh, have the application and the organizations rent a space in your application. Now, in this episode, I build this, uh, basic, uh, application where you could, uh, create organizations and invite the users to be members of this application. And I had a few comments. You can have a look at each of them on, uh, how to build this kind of basic model density application step by step. And the database architecture looked more like this. So a user and individual could be a member of multiple organizations, and in different organizations he could have a different role. And all the downstream resources like projects, tasks or scope to, to the organization. You can think of, uh, a good example of multitenancy when you look at, for example, slack. So here, there are multiple separate workspaces with different users who can have different roles in each of these workspaces. And, uh, you see all the resources are scoped to this workspace. So I go to another workspace, there are other admins. The billing is on a workspace or on the organization level. And, uh, you can also have a look for example, at Discord. Again, the idea is the same. There are multiple workspaces. Each workspace has, uh, uh, users with different roles and you can, uh, invite other users to join, um, an organization. So this is more, less, uh, how, uh, this kind of multitenant, uh, application should work. Now there's also this, uh, wonderful tweet by Matt Swanson, where he talks about, uh, this kind of multitenancy, uh, database architecture can save you, uh, like a hundred thousand dollars. I think it can not only save you a lot of money, but it can like inter factor in, but it can also earn you a lot of money because, uh, uh, B2B uh, organizations are more likely to use your application if they can have team access to resources in your organization. And, uh, talking of this bullet chain, uh, founder Andrew Culver also wrote a wonderful article that I highly recommend you to read. Teams should be an MVP feature of, uh, whatever business application you are building. Now, going back to this, uh, episode, I think it was, uh, quite a good, uh, starter of how Multitenancy should work. But, uh, you just can't cover all the cases, uh, and uh, make this perfect in just a few comments. There's a lot of work, uh, that actually needs to be done to make this, uh, uh, kind of logic bulletproof. So that's why I created Money Gun. You can think of, of it as a multi-tenancy boilerplate or template application where I have, uh, this sort of architecture really well implemented and well tested. So I wrote, uh, tests for all the possible outcomes in the, this kind of architecture so that, uh, it is bulletproof. It's not bullet train, but it is bulletproof. Now, let's have a quick look at how Money Gun works. So let me, uh, restart. I've, uh, got it here. I will start the rail server. Been Dev. Okay, I started the application. I'm going to create an account. So let's create an account. I'm just using the basic device here, and here I have created an account and I can create a workspace or an organization. Let me create an organization. Let's name it Ship Rails, okay? Create organization. So here inside the organization, I've got, uh, users or kind of members of this organization. And I've got one sculpture source that is inboxes. So for example, I want to have an inbox, uh, where I would let users, uh, uh, type which episodes that they want me to talk about, uh, in my future, super Rails, screencast, uh, which episodes do you want in the future? So I've created this inbox, and you see this inbox is scoped to this organization. Now, let's try inviting another user to this organization. I will invite somebody, I will invite him as a member, not as an admin. I'll send the invitation. So the invitation has been sent, the user has been added, uh, as a member, and let me accept this invitation as the other user. So in my locus, uh, I see that the invitation has been sent. I will, uh, copy the link to accept the invitation. I will open it in a new tab in the private tab. So let's, uh, accept the invitation. Okay? And here I'm logged in as this other user. And you see I am, uh, already a member of this organization. I go to the list of users. And you see, I cannot edit the roles of, uh, myself or the other user because I'm just a member. The only thing I can do is leave this, uh, organization. Let's go to inboxes. You see, I don't have access to inboxes. 'cause by default only the organization admin can, uh, access inboxes. Let's try changing this. I will go to inbox policy. I'm using punit to handle, uh, authorization, uh, role-based authorization to different sources. So at the moment I say that only admins can, uh, uh, only members who have the admin role can, uh, edit the organization or an inbox and view everything. Let's say that, uh, any, uh, member can, uh, if you and edit inboxes, again, I'm going to inboxes now, and you see, I as a normal member can, uh, have access to inboxes. I can, uh, edit this inbox. Okay, looks, uh, fine. Now, let's, uh, try, uh, going back to the admin and let's try, let's say editing my role. I will try to go from being an admin to being a member. And you see, I cannot, uh, turn myself into a member because, uh, an organization has to have at least, uh, one admin because otherwise, uh, you won't be able to like, change anybody's roles to lead the organization and so on. So the organization will not be functional if there is no admin. So this is like one small thing that you should think of. An organization should have at least one member, uh, admin. Let's make the other member an admin. And now I think we can change our role to member. Okay, now let's try creating a new organization. So, uh, I'll go to the list of organizations. I'll create an organization named Ong. Here it is. So, uh, you see, I have two organizations here. I'm the admin here, I am just, uh, uh, a member. All I can do is leave this organization. And you see the inboxes are scoped to the organization. So here, organization one has, uh, one inbox, organization two doesn't have any inboxes, and you as a member of both organizations can have, uh, inboxes from both organizations open in two separate tab. So that's the beautiful thing about, uh, uh, root based setting of current, uh, uh, organization that, uh, you can have, uh, two different organizations open into different tabs. So, um, looks fine actually about this solution of organizations. Uh, I really like this kind of sidebar fiction. Uh, obviously it was inspired by Discord and uh, by, uh, slack. And, uh, this, uh, template, uh, application also includes another kind of sidebar that, uh, would not have this kind of double side, but, but just one sidebar. So I have the organization Manning on Open. I see the dashboard uses for it. I can open the dropdown and switch the organization and see the data for that other organization. Okay, let's dive into the code itself. So, uh, usually when I look at a new Ribbon Rose application to me, first of all, I look at the GEM file. Now everything here is just whatever I have. When I create a new Rails, uh, uh, seven or Rails eight application, I just have installed Tailwind. And, uh, here are the unique gems to this application. So I've got device, device, inviable and bonded. Now, uh, I think Rails eight authentication generator isn't ready for production yet. And device plus device Inviable works perfectly for, uh, authenticating users and send invitations to create a new user in the application. Again, I responded for authorizing, uh, uh, organization members, organization members, ems, uh, and organization admins to different resources within the organization. Then for the front end, I've got few component in line SVG to display these SVGs and, uh, active link tool. Active link tool. Basically here I have inboxes. I go deep inside inboxes, and this inboxes, uh, link in the sidebar is still highlighted. And I've also got this gem nested scaffold. Now, this is a really nice one. Let's say I want to create a new nested resource within an organization. Let's say I want to have projects within an organization instead of just doing a scaffold and then having to update all the scaffold views and controllers to be nested within the organization resource. I'm just going to run a command for creating a nested scaffold. So, um, let's try doing it. I have this example, command rail generate nested scaffold within organization. I'm going to have a project, and the project is going to have a name that is a stream. So, uh, let's run this generator rails. Uh, the B migrate, let's have a look at the DB migration. So you see it was automatically scoped to the organization. And, uh, we'll go to our roots. You see the, uh, nested, the scaffold generator had a few like made to assumptions that we want to either namespace or put it within resources organization, but we already have resources organization here lower. So I'm just going to put, uh, resources, projects, inside resources, organization. If we have a look at, uh, our projects, you see they're already scoped to the organization, uh, everywhere. So we don't need to update the controller. Just one minor thing here. We look for the project within all organizations. We want to see, see, we want to look up the project within the organizations where the user is a member. So I'm actually going to remove this set organization completely. And instead of it, I'm going to inherit not from application controller, but from this base organization's controller, where I have a method to set the organization to find the organization from the organizations where the current user is a member. So produce controller and hears from organization base controller. Let's try running the tests. Okay, let's run the migrations rails db migrate rails test. Okay, so tests are failing. This one is failing because, uh, organization has many projects. And then deleting an organization, we should also delete the projects within it. So I will say depend, dependent, destroy. Okay, that test works. Let's open the, uh, projects controller test. So for it to pass, again, you see by default from the scaffold generator from Nest Scaffold Generator, they already have everything set up, even in our test to have a projects within organizations. I'm just going to sign in, uh, a user and I think this test should pass. Okay? We just have one test, not passing inbox. Policy test goes in our inbox. Uh, policy. We said that, uh, currently anybody can access inboxes, but we want only admins to access inboxes for this policy. Let's run the test once again. Okay, let's run our server and try to navigate to, uh, projects within an organization. So, uh, I go to an organization, I want to have the projects in the sidebar. Let's add them to the sidebar. So in this, uh, navigation links partial, I'm going to also add the a link to organization projects. And here we have the projects that are scoped to the current organization. Let's create a new project. Uh, okay, so here we have a project for Super Rails. I will go to another organization. I go to projects, and you see it doesn't have any projects. So projects are scoped within an organization. Looks good. So let's inspect the code a bit more. Uh, so in schema, again, there are quite a lot of migrations for active storage to be able to add an, uh, avatar to an, uh, organization. Let's have a look at an organization. Yeah, we can add an avatar. We have, uh, again, the most important part. So we've got, uh, organizations, we've got users, and the user can be a member of an organization via a membership. And the resources, like projects are scoped to an organization. And Inbox has also scoped to an, uh, organization in the Our Roots. Again, uh, everything that should be scoped to an organization, they also scoped in the roots. This way we don't lose the logical hierarchy and, uh, this kind of, uh, multitenancy that in the Roots works well. Anyway, I'm, uh, really happy about this kind of, uh, boiler plate, multitenancy boiler plate or static kit, whatever. Uh, it has, uh, I think quite a good implementation of, uh, this kind of, uh, um, organizations, memberships and users logic. I have written a lot of tasks to ensure that, uh, it works flawlessly and there are no leaks between data from different, uh, organizations and users can do strange stuff like change, uh, another user's roles. I invite you to have a look, uh, at the, the code here. Maybe you can start your own application based on the, this static kit. Uh, maybe you can, uh, integrate, uh, the, uh, code for, uh, organizations and memberships into your application. Uh, in the future. I'm planning to add a couple of more features, like, uh, being able to accept or decline a membership request to an organization because you can be invited to multiple organizations and not always you want to accept the membership request. And possibly I will add the billing on, on, on an organization level. And if I add the billing, then it's going to be kind of a real, sort as a service, uh, uh, start kit application. Anyway, I invite you to have, uh, a look at Money Gun. Tell me what you think about it.
9
Join the conversation
Sign in to access PRO lessons, access private repos, leave comments, create watch lists.
We collect your email address, name and username to create your account. We do not share your email address with anyone else.